繁体   English   中英

无法使用pthread_kill和sigwait解除阻止/“唤醒”线程

[英]Can't unblock/“wake up” thread with pthread_kill & sigwait

我正在研究C / C ++网络项目,并且在同步/给线程发送信号方面遇到困难。 这是我要完成的工作:

  1. 使用poll函数轮询一堆套接字
  2. 如果在POLLIN事件中准备好任何套接字,则将信号发送到读取器线程和写入器线程以“唤醒”

我有一个称为MessageHandler的类,该类设置信号掩码并生成读取器和写入器线程。 然后,在它们内部,我等待应该唤醒它们的信号。

问题是我正在通过向线程发送信号来测试所有这些功能,但它永远不会唤醒。

这是带有进一步说明的问题代码。 注意,我刚刚强调了它如何与阅读器线程一起工作,因为编写器线程本质上是相同的。

// 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创建读取器线程,其中arg是指向MessageHandler类型的对象的指针。
  4. 我称睡眠为一种便宜的方法,以确保我的readerThread一直执行到sigwait()
  5. 我将信号SIGCONT发送到readerThread,因为我对sigwait感兴趣,一旦收到它就唤醒/取消阻止。
  6. 再次,我将睡眠称为一种廉价的方法,以确保我的readerThread在从sigwait()唤醒/解除阻塞后可以执行所有方法。

其他有用的注释可能有用,但我认为不会影响该问题:

  • 构造MessageHandler,然后在给定要运行的函数指针的情况下创建另一个线程。 此线程将负责创建读取器和写入器线程,使用poll函数轮询套接字,然后可能同时向读取器和写入器线程发送信号。

我知道这是一篇很长的文章,但是非常感谢您阅读并提供任何帮助。 如果我不够清楚,或者您觉得我没有提供足够的信息,请告诉我,我将予以纠正。

再次感谢。

POSIX线程具有条件变量是有原因的。 使用它们。 使用线程编程时,您不需要信号黑客来完成基本的同步任务。

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

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

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

另外,请注意,您的当前方法每次同步都会引发多个系统调用(用户空间/内核空间转换)。 通过良好的pthreads实现,使用条件变量应该最多将其降至一个syscall,并且如果线程之间的相互配合程度足够好以至于当它们仍在用户空间中旋转时发生等待事件,则可能根本不进行任何调用。 。

这种模式似乎有点奇怪,并且很可能容易出错。 pthread库包含许多同步方法,最有可能满足您需求的方法是pthread_cond_*系列。 这些方法处理条件变量 ,这些条件变量实现了Wait and Signal方法。

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

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

R.指出由于额外的内核空间调用而产生了额外的开销。 也许如果您睡眠> N个线程,则单个条件变量将击败多个sigwait和pthread_kills。 在我们的应用程序中,我们只想在工作到达时唤醒一个线程。 您必须为每个线程具有一个条件变量和互斥体才能执行此操作,否则将获得踩踏信号。 在一项测试中,我们睡了N次并唤醒N个线程,信号比互斥量和条件变量高出5倍(原本可能是40倍,但我不记得了。。。) 我们没有测试过一次可以唤醒1个线程的Futex,并且专门对其进行了编码以限制对内核空间的访问。 我怀疑futex会比互斥体快。

暂无
暂无

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

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