【问题标题】:Peek stdin using pthreads使用 pthread 查看标准输入
【发布时间】:2011-07-27 16:46:04
【问题描述】:

我正在尝试查看标准输入以查看是否有任何使用 pthreads 的内容。我(想我)需要这样做,因为如果 std in 中没有任何内容,流访问函数将阻塞输入。

我觉得这样做的方法是启动一个检查标准输入的 pthread,然后 sleep(1) 并查看该线程是否找到任何东西。

这是我目前所拥有的。如果没有任何东西通过管道传输到程序中,那么它将按预期休眠,但如果 stdin 中有内容,则线程永远不会被触发。

#include <iostream>
#include <pthread.h>
#include <stdlib.h>

using namespace std;

void *checkStdIn(void *d){
    char c = '\0';
    c = cin.peek();
    if (c){
        cout << c << endl;
    }
}

int main(int argc, char *argv[]){

    pthread_t thread;
    int rc;
    rc = pthread_create(&thread, NULL, checkStdIn, NULL);
    if (rc){
        cerr << "Error no. " << rc << endl;
        exit(EXIT_FAILURE);
    }
    sleep(2); 

    return 0;
}    

【问题讨论】:

  • 程序在执行线程之前就结束了。
  • 为什么 sleep() 不补救呢?
  • 你的线程检查输入一次,什么也没找到并终止。
  • @Ty:如果要等待线程完成,请使用 phthread_join
  • 忘记我之前的评论,这是错误的。

标签: c++ pthreads stdin


【解决方案1】:

您不需要 pthreads,您可以使用 select(2)poll(2) 来了解您是否可以在不阻塞应用程序的情况下查看标准输入。为此,我编写了 my_peek() 函数,您只需要传递您想要等待输入的秒数(如果您不想等待,您甚至可以传递 0):

/* According to POSIX.1-2001 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include <iostream>

int
my_peek(unsigned int nsecs)
{
    struct timeval timeout;
    fd_set rfds;
    int fd;

    // stdin file descriptor is 0
    fd = 0;

    timeout.tv_sec = nsecs;
    timeout.tv_usec = 0;

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    if (select(fd + 1, &rfds, NULL, NULL, &timeout) > 0)
        return std::cin.peek();
    return -1;
}

int
main(void)
{
    int peek;

    peek = my_peek(2);
    if (peek != -1) {
        std::cout << "we could peek without freezing" << std::endl;
        std::cout << "my_peek() returned " << peek << std::endl;
    } else {
        std::cout << "we could not peek without freezing" << std::endl;
    }

    return 0;
}

请注意,依靠select(2) 来判断cin 对象或stdin FILE 结构中是否有数据是不好的,因为正如Nemo 所说,它们是缓冲的。最好的办法是在此示例中使用read(2) 避免使用“cin”。 my_peek() 的改进版本如下所示:

int
my_peek(unsigned int nsecs)
{
    struct timeval timeout;
    fd_set rfds;
    int fd;
    unsigned char c;

    // stdin file descriptor is 0
    fd = 0;

    timeout.tv_sec = nsecs;
    timeout.tv_usec = 0;

    FD_ZERO(&rfds);
    FD_SET(fd, &rfds);

    if (select(fd + 1, &rfds, NULL, NULL, &timeout) <= 0)
        return -1;
    if (read(fd, &c, 1) != 1)
        return -1;
    return static_cast<int>(c); /* or "return (int)c" for C-only programs */
}

有关更多信息,请查看select(2) 手册页http://linux.die.net/man/2/select

PS:您可以尝试依赖 std::cin.rdbuf()-&gt;in_avail() 返回的值,如 cpluplus 站点 http://www.cplusplus.com/reference/iostream/streambuf/in_avail/ 中所述,甚至在 istream 类的 readsome() 方法中,但它们通常依赖于FILE 未在 GNU C++ 库中公开的缓冲区。不要这样做,否则你可能会失败。

【讨论】:

  • 如果您在支持 POSIX 的平台上,这是迄今为止支持 pthreads 环境的最佳答案。当操作系统可以告诉您文件描述符上的状态时,无需生成线程。 (+1)
  • 除了std::cin 通常是缓冲的,所以这通常不起作用。 (尝试 peek+read 在循环中一次一个字符,从文件重定向标准输入,你会明白我的意思。)
  • 谢谢,尼莫。我修复了示例并添加了避免缓冲区问题的提示。
  • @Fernando 哇,感谢您的回答,效果很好...感谢您的帮助:)
【解决方案2】:

您的sleep(2) 毫无价值,因为它不能保证您的线程将在程序终止前 2 微秒内完成。您需要实现pthread_join(thread, NULL); 以等待线程完成。请参阅此处以获取良好的 pthread 示例。

此外,cin.peek() 将阻塞等待输入。这就是它的设计方式。有关cin.peek 的示例,请参见此处。

【讨论】:

  • sleep 时间实际上是整秒,但无论如何都需要加入。
【解决方案3】:

编辑:呸,费尔南多的忍者 :)

好的,所以我不能 100% 确定最适合您的答案是什么,因为我无法确定您希望您的程序最终做什么

首先,这不是应该使用线程解决的问题。线程并不是万能的解决方案,与其他解决方案相比,线程通常具有巨大的开销。因为您已经在使用pthreads,所以我认为 Windows 兼容性不是问题。

第一步是禁用规范模式,这将允许您获取字符而无需等待enter。如果您 100% 确定 stdin 永远不会成为终端,则可以跳过此步骤。

#include <iostream>
#include <termios.h>

void toggle_canonical(bool on) {
    struct termios terminal_settings;

    // Get the existing terminal settings
    tcgetattr(0 /* stdin */, &terminal_settings);
    if(on) {
        // Disable canonical mode
        terminal_settings.c_lflag &= ~ICANON;
        // Read at least one character.
        terminal_settings.c_cc[VMIN] = 1;
    } else {
        // Enable canonical mode
        terminal_settings.c_lflag |= ICANON;
    }
    tcsetattr(0 /* stdin */, TCSANOW, &terminal_settings);

    return;
}

int main(const int argc, const char* argv[]) {
    // The read timeout, which can be 0 if you don't want to block at all.
    struct timeval to = {5 /* seconds */, 0 /* miliseconds */};
    fd_set read_fds;
    int ret = 0;

    // Turn canonical mode on
    toggle_canonical(true);

    FD_ZERO(&read_fds);
    FD_SET(0, &read_fds);

    // The first parameter to select() is the highest file
    // descriptor in the set + 1, so in this case because
    // STDIN == 0, we pass 1. This is actually completely
    // ignored on several platforms, including Windows.
    if((ret = select(1, &read_fds /* read set */, NULL /* write set */, NULL /* error set */, &to /* timeout */)) == 0) {
        std::cout << "You didn't type anything in time." << std::endl;
    } else if (ret == 1) {
        std::cout << "Yay, you typed something in time!" << std::endl;
    } else if (ret == -1) {
        std::cout << "Oh no, an error occured!" << std::endl;
    }

    // Turn canonical mode off
    toggle_canonical(false);
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2017-05-16
    • 2013-09-18
    • 1970-01-01
    • 2022-07-10
    • 1970-01-01
    相关资源
    最近更新 更多