【问题标题】:Sleeping in a Thread (C / POSIX Threads)在线程中休眠(C / POSIX 线程)
【发布时间】:2010-10-25 16:34:40
【问题描述】:

我正在开发一个使用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() 变体来做到这一点?

【问题讨论】:

    标签: c multithreading pthreads sleep usleep


    【解决方案1】:

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

    【讨论】:

    • select 是一个unix惯用的小睡眠(可以用fifo戳)
    【解决方案2】:

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

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

    【讨论】:

    • sema_wait 和 sema_post 是为了同步,虽然我的问题是同步的,但不是互斥问题。
    • 阅读链接的文章?信号量也用于发信号,并不是因为大多数人将它们误用作互斥体,所以它们不能用于其真正目的。
    • 不管你睡什么,条件本身可能就是一个信号量。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • sleepusleepSIGALRM 的实现不符合 POSIX。
    【解决方案4】:

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

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

    【讨论】:

    • 这也是我推荐的。
    【解决方案5】:

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

    【讨论】:

    • 因为我使用 usleep() 只是为了暂停,所以我不希望使用条件变量(和额外的互斥锁)。
    【解决方案6】:

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

    nanosleep(2)

    signal(7)

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

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

    【讨论】:

      【解决方案7】:

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

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

      【讨论】:

        【解决方案8】:

        也许您需要弄乱信号掩码,或者信号无法从睡眠中消失...我不知道。我不知道可以使用 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);
        

        【讨论】:

          猜你喜欢
          • 2019-08-12
          • 2011-09-12
          • 1970-01-01
          • 1970-01-01
          • 2011-09-18
          • 1970-01-01
          • 2011-06-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多