无法使用pthread_kill& amp解锁/“唤醒”线程调用sigwait

时间:2011-04-05 18:57:21

标签: c++ c multithreading synchronization

我正在开发一个C / C ++网络项目,并且在同步/发信号通知我的线程时遇到了困难。以下是我要完成的任务:

  1. 使用民意调查功能
  2. 轮询一堆套接字
  3. 如果POLLIN事件中有任何套接字准备好,则向读取器线程和写入线程发送信号以“唤醒”
  4. 我有一个名为MessageHandler的类,它设置信号掩码并生成读写器线程。在他们内部,我然后等待应该唤醒他们的信号。

    问题在于我通过向线程发送信号来测试所有这些功能,但它从未唤醒过。

    以下是问题代码并进一步说明。注意我刚刚强调了它如何与reader线程一起工作,因为writer线程基本相同。

    // Called once if allowedSignalsMask == 0 in constructor
    // STATIC
    void MessageHandler::setAllowedSignalsMask() {
         allowedSignalsMask = (sigset_t*)std::malloc(sizeof(sigset_t));
         sigemptyset(allowedSignalsMask);
         sigaddset(allowedSignalsMask, SIGCONT);
    }
    
    // STATIC
    sigset_t *MessageHandler::allowedSignalsMask = 0;
    
    // STATIC
    void* MessageHandler::run(void *arg) {
        // Apply the signals mask to any new threads created after this point
        pthread_sigmask(SIG_BLOCK, allowedSignalsMask, 0);
    
        MessageHandler *mh = (MessageHandler*)arg;
        pthread_create(&(mh->readerThread), 0, &runReaderThread, arg);
    
        sleep(1); // Just sleep for testing purposes let reader thread execute first
        pthread_kill(mh->readerThread, SIGCONT);
        sleep(1); // Just sleep for testing to let reader thread print without the process terminating
    
        return 0;
    }
    
    // STATIC
    void* MessageHandler::runReaderThread(void *arg) {
        int signo;
        for (;;) {
                sigwait(allowedSignalsMask, &signo);
    
                fprintf(stdout, "Reader thread signaled\n");
        }
    
        return 0;
    }
    

    我取出了代码中的所有错误处理来压缩它,但确实知道线程正常启动并进入sigwait调用。

    错误可能是显而易见的(它不是语法错误 - 上面的代码是从可编译的代码中压缩出来的,我可能会在编辑它时搞砸了)但是我似乎无法找到/看到它因为我花了在这个问题上花了太多时间,让自己感到困惑。

    让我解释一下我认为我在做什么以及它是否有意义。

    1. 在创建MessageHandler类型的对象时,它会将allowedSignalsMask设置为我有兴趣用来唤醒我的线程的一个信号(暂时)的集合。
    2. 我使用pthread_sigmask将信号添加到当前线程的阻塞信号。在此之后创建的所有其他线程现在应该具有相同的信号掩码。
    3. 然后我使用pthread_create创建reader线程,其中arg是指向MessageHandler类型对象的指针。
    4. 我称睡眠是一种廉价的方式,以确保我的readerThread一直执行到sigwait()
    5. 我将信号SIGCONT发送给readerThread,因为我对sigwait感兴趣,一旦接收它就会唤醒/解锁。
    6. 我再次将睡眠称为一种廉价的方式,以确保我的readerThread可以在从sigwait唤醒/解除阻塞之后一直执行()
    7. 其他有用的注释可能有用,但我认为不会影响问题:

      • 构造MessageHandler,然后根据指向运行的函数指针创建不同的线程。该线程将负责创建读写器线程,使用poll函数轮询套接字,然后可能向读写器线程和编写器线程发送信号。

      我知道它很长,但感谢您阅读它以及您可以提供的任何帮助。如果我不够清楚,或者您觉得我没有提供足够的信息,请告诉我,我会更正帖子。

      再次感谢。

3 个答案:

答案 0 :(得分:5)

POSIX线程有条件变量是有原因的;使用它们。在使用线程编程时,你不应该需要信号hackery来完成基本的同步任务。

这是一个很好的pthread教程,其中包含使用条件变量的信息:

https://computing.llnl.gov/tutorials/pthreads/

或者,如果您对信号量更熟悉,则可以使用POSIX信号量(sem_initsem_postsem_wait)。但是一旦你弄清楚为什么条件变量和互斥配对有意义,我想你会发现条件变量是一个更方便的原语。

另外,请注意,您当前的方法会在每次同步时产生多个系统调用(用户空间/内核空间转换)。有了良好的pthreads实现,使用条件变量应该将其丢弃到最多一个系统调用,如果你的线程相互保持足够好以至于当它们仍然在用户空间中旋转时发生等待事件,则可能根本没有

答案 1 :(得分:1)

这种模式似乎有点奇怪,而且很可能容易出错。 pthread库具有丰富的同步方法,最有可能满足您pthread_cond_*系列需求的方法。这些方法处理condition variables,它实现了等待和信号方法。

答案 2 :(得分:0)

使用 SIGUSR1 代替SIGCONT。 SIGCONT不起作用。也许信号专家知道原因。

顺便说一下,我们使用这种模式,因为条件变量和互斥量对于我们的特定应用来说太慢了。我们需要非常快速地睡眠和唤醒单个线程。

R上。指出由于额外的内核空间调用会产生额外的开销。也许如果你睡觉> N个线程,然后单个条件变量将击败多个sigwaits和pthread_kills。在我们的应用程序中,我们只想在工作到来时唤醒一个线程。你必须为每个线程都有一个条件变量和互斥量,否则就会发生踩踏事件。在我们睡眠和唤醒N次M次M次的测试中,信号击败了互斥量和条件变量5倍(它可能是40倍,但我不记得了...... argh)。我们没有测试其中可以一次唤醒1个线程的Futex,并且特别编码以限制到内核空间的行程。我怀疑futexes会比互斥量更快。