繁体   English   中英

从多线程进程正常退出

[英]Exiting gracefully from a multithreaded process

我正在运行多线程C程序(进程?),它使用了信号灯和pthread。 线程在标准输出上持续交互,阻塞,唤醒和打印提示,而无需任何人工干预。 我希望能够通过按#之类的键盘字符来退出此过程(在打印一条消息并放下所有线程之后,而不是通过粗糙的CTRL+C SIGINT )。

我从用户那里获得这种输入的选择是什么?

我还能提供哪些其他相关信息来帮助解决此问题?

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

从线程读取标准输入没有什么区别,除非有多个线程试图同时读取它。 不过,很可能您的线程并不是所有调用函数都始终读取标准输入。

如果您经常需要从用户那里读取输入,则可能需要一个线程来读取该输入,然后根据该输入设置标志或将事件发布到其他线程。

如果您只想使用kill字符,或者仅将其用于调试,那么您可能想要做的就是偶尔轮询标准输入上的新数据。 您可以通过将标准输入设置为非阻塞来执行此操作,并偶尔尝试读取它。 如果读取返回0个字符,则没有按键被按下。 但是,此方法存在一些问题。 在将基础文件描述符(int)设置为非阻塞之后,我从未在FILE *上使用过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);
}

您可以在您的一个线程中调用此函数,直到只要将其调整为0,您就可以继续进行下去。 当它返回一个正数时,您需要从stdin中读取一个字符以查看其含义。 请注意,如果您在其他地方的stdin上使用stdio函数,则实际上可能在新字符前面已经缓冲了其他字符。 poll告诉您操作系统对您来说是新东西,而不是C的stdio

如果您经常从其他线程的标准输入中读取内容,那么事情就会变得混乱。 我假设您没有这样做(因为如果您这样做并且工作正常,则可能不会问这个问题)。

您将有一个线程在监听键盘输入,然后在接收#作为输入时将join()其他线程。

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

我要做的方法是保留一个全局int“ should_die”或范围为0或1的内容,以及另一个全局int“ died”,它跟踪终止的线程数。 should_die和dead最初都是零。 您还需要两个信号量来在全局变量周围提供互斥量。

在某个时候,线程会检查should_die变量(当然是在获取互斥量之后)。 如果它应该死,它将获取dead_mutex,增加死亡人数,释放dead_mutex,然后死亡。

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

如果主线程没有产生所有线程本身,则可以做一个小修改,使“ threads_alive”代替“死”。 当线程派生时,threads_alive会增加;当线程死掉时,threads_alive会减少。

通常,彻底终止多线程操作是一个麻烦,除了特殊情况下,您可以使用诸如信号量屏障设计模式之类的东西之外,这是我所听说的最好的方法。 如果您找到一个更好,更干净的产品,我很想听听。

〜anjruu

通常,我有线程在等待一组事件,这些事件之一是终止事件。

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

实际上, SIGINT并不难处理,通常用于正常终止。 您需要一个信号处理程序和一种方法来告知所有线程该停止了。 线程可以在其循环中检查一个全局标志,并设置信号处理程序。 尽管您需要一种从终端获取输入的方法,但相同的方法也适用于“按用户命令”终止-在专用线程中进行轮询,或者再次设置终端以为您生成信号。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM