12

我正在开发一个使用POSIX Threads的多线程应用程序。我正在使用线程来执行定期工作,为此我正在使用usleep(3)来暂停线程执行。我的问题是如何从主线程取消 usleep() 计时器,我尝试过,pthread_kill(thread, SIGALRM)但它具有全局影响,导致主应用程序终止(默认情况下)。这是我的伪代码:

void threaded_task(void *ptr) {
    initialize();

    while(running) {
        do_the_work();
        usleep(some_interval);
    }

    clean_up();
    release_resources();
}

这是用于从主线程停止(并正常关闭)给定线程的伪函数:

void stop_thread(pthread_t thread) {
    set_running_state(thread, 0); // Actually I use mutex staff
    // TODO: Cancel sleep timer so that I will not wait for nothing.
    // Wait for task to finish possibly running work and clean up 
    pthread_join(thread, NULL);
}

实现我的目标的便捷方式是什么?我必须使用条件变量还是可以使用 sleep() 变体来做到这一点?

4

8 回答 8

9

与FIFOselect()或套接字一起使用,您可以戳它以唤醒它。

于 2010-10-25T16:36:36.953 回答
6

你也可以用信号量睡觉(这实际上是它们的真正目的)。

asema_wait在您的线程中和 asema_post在您的主线程中。它很简单,很干净,很便携。这里是详细说明该过程的文章的链接:http: //www.netrino.com/node/202

于 2010-10-25T18:13:55.327 回答
5

SIGALRM 杀死整个应用程序的原因是您可能还没有为它注册信号处理程序。SIGALRM 的默认操作是让内核终止进程,因此如果usleep以不使用 SIGALRM 的方式实现(nanosleep例如,使用超时轮询函数或其中一个轮询函数)则usleep不会注册处理程序或以其他方式更改了信号的默认配置。

void handle_alrm(int sig) {
}

...

int main(void) {
    signal(SIGALRM, handle_alrm);
    ...

应该足以避免杀死您的程序,尽管您应该研究更复杂的sigaction功能,而不是signal因为它允许更多控制并且在不同平台上的行为更加一致。

usleep如果您随后尝试在使用 SIGALRM 来实现or的系统上使用代码,这可能会导致问题sleep,因此您可能只想不使用这些代码的标准库版本并使用在所有平台上具有更可预测实现的函数(可能是一个nanosleep提供您想要的界面的薄包装器)。

于 2010-10-25T17:50:39.793 回答
4

我们使用 pthread_cond_timedwait 对具有超时的条件变量进行等待

当我们想要关闭时,我们设置一个“关闭”变量并执行 pthread_cond_broadcast

于 2010-10-25T17:49:52.830 回答
2

作为 的替代方法select,也可以使用 pthread 条件变量(请参阅pthread_cond_initpthread_cond_waitpthread_cond_signal)、SysV 信号量或 POSIX 信号量。对于事件处理线程,所有这些都比 usleep 更合适。

于 2010-10-25T16:44:00.283 回答
2

从引用的手册页看来,您正在 Linux 上运行。您应该能够使用 nanosleep 并通过确定的应用程序 (SIGRTMIN+x) 中断子进程。Nanosleep 具有被信号中断并返回它应该睡觉的剩余时间的功能。如果您使用更长的时间睡觉,您也可以只使用睡眠。

纳米睡眠(2)

信号(7)

上面提到的任何类型的 IPC 也可以帮助您解决这个问题。

编辑:看起来你已经在这样做了,除非你应该使用一个不会对程序产生外部影响的信号。任何睡眠功能都应被非阻塞信号中断。实时信号旨在基于每个应用程序使用。

于 2010-10-25T16:49:29.420 回答
2

有多种方法可以做到这一点:

  • 使用@Ignacio 提到的自管道技巧(Linux 提供了方便但不可移植的eventfd(2)方式来替换此处的管道)
  • 通过围绕互斥锁和条件变量构建的阻塞队列连接线程,等待空队列,唤醒队列中的项目
  • 在启动其他线程之前阻塞主线程中的信号,等待信号,唤醒信号 - 见pthread_sigmask(3)
于 2010-10-25T17:05:20.053 回答
2

也许您需要弄乱信号掩码,或者信号无法摆脱睡眠状态……我不知道。我不知道可以使用 sigwait() 或 sigtimedwait()。我们使用 pthread_kill 来唤醒线程,但我们使用 sigwait 来休眠它们……而不是 usleep。这是我发现的最快的唤醒方式(根据我的测试,比等待 pthread_cond 快 40-50 倍。)

我们在创建线程之前这样做:

int fSigSet;
sigemptyset(&fSigSet);
sigaddset(&fSigSet, SIGUSR1);
sigaddset(&fSigSet, SIGSEGV);
pthread_sigmask(SIG_BLOCK, &fSigSet, NULL);

创建的每个线程都继承此掩码。我对面具有点困惑。你要么告诉系统不要对某些信号做任何事情,要么你告诉系统你正在处理一些信号......我不知道。其他人可以通过管道帮助我们。如果我更好地了解掩码的工作方式,我可能会告诉您,您可以将上述代码粘贴到您的 ThreadProc 中。另外,我不确定是否需要 SIGSEGV。

然后一个线程调用它自己休眠:

int fSigReceived;
// next line sleeps the thread
sigwait(&fSigSet, &fSigReceived);  // assuming you saved fSigSet from above...
// you get here when the thread is woken up by the signal
// you can check fSigReceived if you care what signal you got.

然后你这样做来唤醒一个线程:

thread_kill(pThread, SIGUSR1);
于 2011-01-13T01:56:32.850 回答