发信号通知进程中的所有线程

时间:2010-11-17 14:36:55

标签: c multithreading pthreads posix signals

在不保留当前线程列表的情况下,我试图看到实时信号被传递到我进程中的所有线程。我的想法就是这样:

  • 最初安装信号处理程序,并在所有线程中解除信号阻塞。
  • 当一个线程想要发送'广播'信号时,它会获取互斥锁并设置广播正在进行的全局标记。
  • 发件人为自己阻止信号(使用pthread_sigmask),并重复调用raise(sig)进入循环,直到sigpending表示信号处于待处理状态(没有剩余的线程与信号受阻)。
  • 当线程接收到信号时,它们会对其进行操作,但在信号处理程序中等待广播标志被清除,以便信号保持屏蔽状态。
  • 发件人通过解锁信号来完成循环(为了获得自己的交付)。
  • 当发件人处理自己的信号时,它会清除全局标志,以便所有其他线程可以继续他们的业务。

我遇到的问题是pthread_sigmask没有受到尊重。如果我在strace下运行测试程序(可能是由于不同的调度时间),一切正常,但是一旦我单独运行,发送者就会收到自己的信号(尽管已经阻止了它?)并且没有其他线程的安排。

任何想法可能出错?我尝试使用sigqueue代替raise,探测信号掩码,在整个地方添加sleep以确保线程耐心地等待他们的信号等等。现在我我不知所措。

编辑:感谢psmears的回答,我想我明白了这个问题。这是一个潜在的解决方案。反馈会很棒:

  • 在任何给定时间,我都可以知道正在运行的线程数,如果需要,我可以阻止所有线程创建和退出广播信号。
  • 想要进行广播信号的线程获取一个锁(因此没有其他线程可以同时执行),然后阻止信号为自己,并向进程发送num_threads信号,然后解除阻塞信号为自己。
  • 信号处理程序以原子方式递增计数器,并且信号处理程序的每个实例都等待,直到该计数器等于num_threads才能返回。
  • 执行广播的线程也等待计数器到达num_threads,然后释放锁定。

一个可能的问题是,如果内核内存不足,信号将不会排队(Linux似乎有这个问题)。你知道sigqueue是否可以在呼叫者无法排队信号时可靠地通知呼叫者(在这种情况下我会循环直到它成功),或者信号可能会无声地丢失?

编辑2:现在似乎正在运作。根据{{​​1}}的文档,如果它无法对信号进行排队,则返回sigqueue。但为了提高稳健性,我决定继续调用EAGAIN,直到sigqueue信号处理程序正在运行,并在发送num_threads-1信号后将呼叫交错到sched_yield

在线程创建时有一个竞争条件,计算新线程,但我用一个奇怪的(ab)使用读写锁解决了它。线程创建是“读取”,广播信号是“正在写入”,所以除非有线程试图广播,否则它不会在创建线程时产生任何争用。

4 个答案:

答案 0 :(得分:4)

raise()将信号发送到当前线程(仅),因此其他线程将不会接收它。我怀疑strace使事情有效的事实是strace中的一个错误(由于它的工作方式,它最终拦截发送到进程的所有信号并重新提升它们,所以它可能是以错误的方式重新提升它们......)。

您可以使用kill(getpid(), <signal>)将信号整体发送到当前进程。

然而,您可能会看到的另一个潜在问题是sigpending()可以在所有线程收到信号之前指示信号在进程中处于待处理状态 - 这意味着至少有一个这样的信号待处理进程,并没有CPU可用于运行线程来提供它......

您能描述一下您希望实现的目标的更多细节吗?你想要的便携性如何?几乎可以肯定有一种更好的方法(信号几乎总是一个令人头痛的问题,特别是当与线程混合时...)

答案 1 :(得分:2)

在多线程程序中,raise(sig)相当于pthread_kill(pthread_self(),sig)。 尝试kill(getpid(),sig)

答案 2 :(得分:0)

鉴于您可以显然锁定线程创建和销毁,您是否可以让“广播”线程在每个线程队列中发布线程本地状态所需的更新,每个线程在每次线程使用时都会检查线程本地状态?如果有未完成的更新,则首先应用它们。

答案 3 :(得分:0)

您正在尝试同步一组线程。 从设计模式的角度来看,针对您的问题的pthread本机解决方案将是一个pthread障碍。