Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/powerbi/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 从多线程进程中优雅地退出_C_Pthreads_Semaphore - Fatal编程技术网

C 从多线程进程中优雅地退出

C 从多线程进程中优雅地退出,c,pthreads,semaphore,C,Pthreads,Semaphore,我正在运行一个多线程C程序(进程?),使用信号量和pthreads。线程在stdout上不断地进行交互、阻塞、唤醒和打印提示,无需任何人工干预。我希望能够通过按下像#这样的键盘字符退出此过程(在打印消息并放下所有线程之后,而不是通过原始的CTRL+CSIGINT) 我从用户那里获得这样的输入有哪些选择 我还可以提供哪些有助于解决此问题的相关信息 编辑: 你所有的答案听起来都很有趣,但我的主要问题仍然存在。当我不知道当前正在执行哪个线程时,如何获取用户输入?此外,如果通过SIGINT发出信号,则使

我正在运行一个多线程C程序(进程?),使用信号量和pthreads。线程在stdout上不断地进行交互、阻塞、唤醒和打印提示,无需任何人工干预。我希望能够通过按下像#这样的键盘字符退出此过程(在打印消息并放下所有线程之后,而不是通过原始的
CTRL+C
SIGINT

我从用户那里获得这样的输入有哪些选择

我还可以提供哪些有助于解决此问题的相关信息

编辑:
你所有的答案听起来都很有趣,但我的主要问题仍然存在。当我不知道当前正在执行哪个线程时,如何获取用户输入?此外,如果通过
SIGINT
发出信号,则使用
sem_wait()
的信号量阻塞会中断,这可能会导致死锁。

您将有一个线程监听键盘输入,然后它将在接收到作为输入的其他线程时加入()


另一种方法是创建并使用它来处理应用程序的关闭。

我要做的方法是保留一个全局int“应该死”或其他值,其范围是0或1,另一个全局int“死”,它跟踪终止的线程数。“应该死”和“死”最初都为零。您还需要两个信号量来提供全局互斥

在某一点上,线程检查should_die变量(当然是在获取互斥锁之后)。如果它应该死掉,它将获取死掉的\u互斥体,增加死掉的计数,释放死掉的\u互斥体,然后死掉

主初始线程会定期唤醒,检查已死亡的线程数是否小于线程数,然后返回睡眠状态。当所有其他线程都签入时,主线程死亡

如果主线程本身没有生成所有线程,那么一个小的修改就是将“threads\u alive”改为“dead”。当线程分叉时,线程_alive将递增,当线程消亡时,线程_alive将递减

一般来说,干净地终止多线程操作是一件痛苦的事情,除了可以使用信号量屏障设计模式之类的特殊情况之外,这是我听说过的最好的。如果你能找到一个更好更干净的,我很想听


~anjruu

通常,我有线程在等待一组事件,其中一个事件是终止事件


在主线程中,当我触发终止事件时,我会等待所有退出的线程。

SIGINT
实际上并不难处理,通常用于优雅的终止。您需要一个信号处理程序和一种告诉所有线程该停止的方法。线程在其循环和信号处理程序集中检查的一个全局标志可能会执行此操作。同样的方法也适用于“on user command”终止,不过您需要一种从终端获取输入的方法—在专用线程中轮询,或者再次设置终端为您生成信号


棘手的部分是解锁等待的线程。您必须仔细设计通知协议,告诉谁停止以及他们需要做什么-将虚拟消息放入队列,设置标志并向cv发送信号,等等。

从线程读取标准输入没有区别,除非多个线程同时尝试读取它。不过,您的线程很可能不是一直在调用函数来读取标准输入

如果您经常需要从用户处读取输入,您可能希望有一个线程只读取此输入,然后根据此输入设置标志或向其他线程发布事件

如果kill字符是您唯一想要的,或者如果这只是用于调试,那么您可能需要偶尔轮询标准输入上的新数据。您可以通过将标准输入设置为非阻塞并偶尔尝试从中读取来实现这一点。如果读取返回0个字符读取,则未按下任何键。不过,这种方法也有一些问题。在将底层文件描述符(int)设置为非阻塞后,我从未在
文件*
上使用过stdio.h函数,但我怀疑它们的行为可能很奇怪。您可以避免使用stdio函数,并使用
read
来避免这种情况。我曾经读到过一个问题,如果您派生并执行了一个可以访问该文件描述符版本的新程序,那么块/非块标志可能会被另一个进程更改。我不确定这是否是所有系统上的问题。可以通过“fcntl”调用设置或清除非阻塞模式

但是您可以使用一个具有非常小(0)超时的轮询函数来查看是否有数据准备就绪。
poll
系统调用可能是最简单的,但也有
select
。各种操作系统都有其他轮询功能

#include <poll.h>

...
/* return 0 if no data is available on stdin.
        > 0 if there is data ready
        < 0 if there is an error
*/
int poll_stdin(void) {
    struct pollfd pfd = { .fd = 0, .events = POLLIN };

    /* Since we only ask for POLLIN we assume that that was the only thing that
     * the kernel would have put in pfd.revents */
    return = poll(&pfd, 1, 0);
}
#包括
...
/*如果stdin上没有可用数据,则返回0。
>如果有数据准备就绪,则为0
如果出现错误,则小于0
*/
int poll_stdin(无效){
结构pollfd pfd={.fd=0,.events=POLLIN};
/*因为我们只要求波林,所以我们认为这是唯一
*内核会加入pfd.revents*/
返回=轮询(&pfd,1,0);
}
您可以在一个线程内调用此函数,直到它重新运行0为止,只要它继续运行。当它返回一个正数时,您需要从stdin中读取一个字符来查看它是什么。请注意,如果您在
stdin
的其他位置使用stdio函数,则实际上可能有其他字符已缓冲在新字符前面
poll
告诉您操作系统为您提供了一些新功能,而不是C的stdio

如果您经常在其他线程中读取标准输入,那么事情就会变得一团糟。