我正在运行一个多线程的C程序(进程?),利用信号量&并行线程。线程保持交互,阻塞,唤醒和在没有任何人为干预的情况下,连续打印提示。我希望能够通过按下像#这样的键盘字符来退出此过程(优雅地打印消息并放下所有线程,而不是通过粗略的CTRL+C
SIGINT
)。
我从用户那里获得这样的输入有哪些选择?
我可以提供哪些更有用的信息来帮助解决这个问题?
修改
你的所有答案听起来都很有趣,但我的主要问题依旧。当我不知道当前正在执行哪个线程时,如何获得用户输入?此外,如果通过sem_wait()
发出信号,则使用SIGINT
的信号量阻塞会中断,这可能会导致死锁。
答案 0 :(得分:1)
从线程读取标准输入没有区别,除非多个线程同时尝试读取它。但是,很可能你的线程并不是所有的调用函数都能一直读取标准输入。
如果您经常需要读取用户的输入,您可能希望有一个线程只读取此输入,然后根据此输入设置标记或将事件发布到其他线程。
如果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 。
如果你经常在其他线程中读取标准输入,那么事情就会变得混乱。我假设你没有这样做(因为如果你是,它的工作正常,你可能不会问这个问题。)
答案 1 :(得分:0)
你会有一个线程监听键盘输入,然后它会在接收#作为输入时加入()其他线程。
另一种方法是trap SIGINT并使用它来处理应用程序的关闭。
答案 2 :(得分:0)
我这样做的方法是保持一个全局int“should_die”或其他东西,其范围是0或1,另一个全局int“死亡”,它跟踪终止的线程数。 should_die和death最初都是零。你还需要两个信号量来在全局变量周围提供互斥量。
在某个时刻,线程会检查should_die变量(当然,在获取互斥锁之后)。如果它应该死亡,它会获得去除死亡数,增加死亡数,释放去死亡数,然后死亡。
主要初始线程会定期唤醒,检查已经死亡的线程数是否少于线程数,然后重新进入休眠状态。当所有其他线程都已签入时,主线程将死亡。
如果主线程本身没有产生所有线程,那么一个小的修改就是“threads_alive”而不是“death”。 threads_alive在线程分叉时递增,在线程死亡时递减。
一般来说,干净地终止多线程操作是一个痛苦的屁股,除了特殊情况下你可以使用信号屏障设计模式之类的东西,这是我所听到的最好的。如果你找到一个更好,更清洁的人,我很乐意听到。
〜anjruu
答案 3 :(得分:0)
通常,我有一些线程在等待一组事件,其中一个事件就是终止事件。
在主线程中,当我触发终止事件时,我等待已经退出的所有线程。
答案 4 :(得分:0)
SIGINT
实际上并不难以处理,通常用于优雅终止。你需要一个信号处理程序和一种方法来告诉所有线程它是时候停止了。线程检查其循环和信号处理程序集的一个全局标志可能会这样做。相同的方法适用于“用户命令”终止,但您需要一种从终端获取输入的方法 - 在专用线程中轮询,或者再次设置终端为您生成信号。
棘手的部分是取消阻塞等待的线程。你必须仔细设计谁告诉谁停止以及他们需要做什么的通知协议 - 将虚拟消息放入队列,设置标志并发信号等等。