fgetc()和其他输入函数可以返回。这可以模拟控制台应用程序从键盘上键入Ctrl-D(至少在unix上)读取。但是如何以编程方式进行呢?例如,如何从以下代码中的读者线程中的fgetc()返回(注意:忽略可能的竞争条件)?
#include <pthread.h>
#include <stdio.h>
void* reader()
{
char read_char;
while((read_char = fgetc(stdin)) != EOF) {
;
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t thread;
pthread_create(&thread, NULL, reader, NULL);
// Do something so the fgetc in the reader thread will return
pthread_exit(NULL);
}
谢谢!
答案 0 :(得分:3)
当发生某些事件来处理该事件时,您似乎希望线程在fgetc(stdin)
上停止阻塞。如果是这种情况,您可以在select()
和其他一些消息管道上stdin
,以便线程可以处理来自两者的输入:
fd_set descriptor_set
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(pipefd, &descriptor_set);
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0)
{
// select() error
}
if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
// read byte from stdin
read(STDIN_FILENO, &c, 1);
}
if (FD_ISSET(pipefd, &descriptor_set))
// Special event. Do something else
另请注意,您的流程中只有一个帖子应该从stdin
读取。
答案 1 :(得分:1)
你可以'关闭'标准输入,或者将标准输入连接到'/ dev / null'(Windows上为'NUL:')和freopen()
,或者你可以将标准输入连接到'/ dev / zero ”。
如果关闭它,每个函数调用都将失败,因为文件流无效。如果将其连接到空数据源,则所有读取都将失败并立即返回EOF。如果将其连接到零数据源,则每次读取都将成功并返回相应数量的零字节。
其中一种可能足以满足您的需求。如果没有,那么您可能需要向我们提供您实际需要的更详细的解释。
答案 2 :(得分:0)
Alexandre发布了正确的解决方案。他的回答恰恰回应了我提出的问题。它遵循简单的自编译代码,基于他的提示:
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/select.h>
static int pipe_fds[2];
void* user_interaction()
{
char read_char;
fd_set descriptor_set;
FD_ZERO(&descriptor_set);
FD_SET(STDIN_FILENO, &descriptor_set);
FD_SET(pipe_fds[0], &descriptor_set);
while(1)
{
if (select(FD_SETSIZE, &descriptor_set, NULL, NULL, NULL) < 0) {
// select() error
}
if (FD_ISSET(STDIN_FILENO, &descriptor_set)) {
// read byte from stdin
read(STDIN_FILENO, &read_char, 1);
}
if (FD_ISSET(pipe_fds[0], &descriptor_set))
// Special event. break
break;
}
pthread_exit(NULL);
}
int main(void)
{
pipe(pipe_fds);
pthread_t thread;
pthread_create(&thread, NULL, user_interaction, NULL);
// Before closing write pipe endpoint you are supposed
// to do something useful
sleep(5);
close(pipe_fds[1]);
pthread_join(thread, NULL);
pthread_exit(NULL);
}
答案 3 :(得分:0)
使用POSIX,您可以发信号通知其基元(例如“读取”)正在阻塞的线程,如果您已设置无操作信号处理程序且SA_RESTART位已清除,则基元将因EINTR错误而失败。这是原版的工作版本:
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
static void SignalHandler(int signum)
{
}
void* reader(void *arg)
{
char read_char;
while((read_char = fgetc(stdin)) != EOF) {
;
}
printf("leaving reader\n");
return NULL;
}
int main(int argc, const char * argv[])
{
struct sigaction action;
memset(&action, 0, sizeof(action)); // SA_RESTART bit not set
action.sa_handler = SignalHandler;
sigaction(SIGUSR1, &action, NULL);
pthread_t thread;
pthread_create(&thread, NULL, reader, NULL);
sleep(1); // time to start reader thread
// Do something so the fgetc in the reader thread will return
pthread_kill(thread, SIGUSR1);
sleep(1); // time to exit reader thread; could join it if set up
return 0;
}
答案 4 :(得分:0)
我尝试以稍微修改的形式使用this answer中的代码:
void *user_interaction()
{
char ch;
int rv;
fd_set set;
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(pipe_fds[0], &set);
while (1)
{
rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
if (rv < 0)
{
printf(">>> select(): error occurred, %d\n", rv);
break;
}
if (FD_ISSET(pipe_fds[0], &set))
{
printf(">>> pipe_fds[0]: is ready\n");
break;
}
if (FD_ISSET(STDIN_FILENO, &set))
{
read(STDIN_FILENO, &ch, 1);
write(STDOUT_FILENO, &ch, 1);
}
}
pthread_exit(NULL);
}
但是没有得到预期的行为。如下所示执行时:
$ echo -n 1 | ./a.out
我的终端在无限循环中使用1
进行渲染,select()
从未报告该管道准备就绪(即即使在主线程中close()
将其准备就绪) )。
通过一些实验,我发现您需要在循环内移动FD_ZERO
/ FD_SET
,以使select()
能够按预期工作:
void *user_interaction()
{
char ch;
int rv;
fd_set set;
while (1)
{
FD_ZERO(&set);
FD_SET(STDIN_FILENO, &set);
FD_SET(pipe_fds[0], &set);
rv = select(pipe_fds[0] + 1, &set, NULL, NULL, NULL);
if (rv < 0)
{
printf(">>> select(): error occurred, %d\n", rv);
break;
}
if (FD_ISSET(pipe_fds[0], &set))
{
printf(">>> pipe_fds[0]: is ready\n");
break;
}
if (FD_ISSET(STDIN_FILENO, &set))
{
read(STDIN_FILENO, &ch, 1);
write(STDOUT_FILENO, &ch, 1);
}
}
pthread_exit(NULL);
}