终止线程池

时间:2014-10-10 09:09:01

标签: c pthreads signals ipc

我正在尝试创建一个程序,通过一个公共队列(如它所代表的POSIX消息队列)从各种应用程序接收消息,并将这些命令分派给一个工作线程池。

在过去的几天里,我的设计变得比我记忆中的要多,所以我认为我会来这里寻求建议,特别是如何终止该计划。

我希望通过队列接收终止事件,并由工作人员确认(这样调度员就不会打扰预处理命令,这可能意味着验证检查由工作人员完成)。

因此,我需要一种方法让信息以某种方式从工人回到调度员,告诉后者停止。

在我当前的迭代中,主线程是调度程序线程,从队列接收并与工作者交换信号(通过条件变量)以通知空闲线程挂起的命令。我看到它的方式,信号必须进入图片:

  • 如果队列是非阻止的,调度员将在收到mq_notify后调用(按顺序)mq_receivesigwaitEAGAIN;
  • 如果队列阻塞,调度员只需拨打mq_receive,当信号中断时,EINTR将返回while(atomic_load(state) != POOL_TERMINATING)

在这两种情况下,调度程序都将运行if (ret == EAGAIN),它将由验证终止命令并发出信号的工作人员设置。

现在提出问题:

  1. 使用信号和线程时有什么我需要担心的吗?除此之外,这不是一项微不足道的练习吗? (sample
  2. 我是否有理由为调度员打扰非阻塞队列,因为它唯一的工作就是转发消息?
    • 如果有的话,有人可以从我的偏执中治愈我,担心会在sigwait()POOL_TERMINATING之间收到“新消息”通知吗?
  3. 如果我选择阻止队列,我可以不使用处理程序吗?即工作人员设置mq_receive,发送信号,如果调度员正在接收,EINTR只返回mq_receive(我还不知道有什么信号可以告诉系统“中断一个名为{{1}}的调用,但不要打扰处理程序”,所以我猜这将是一个空的处理程序。)
  4. 我在做什么错?
  5. 提前致谢。

2 个答案:

答案 0 :(得分:1)

您在sigwait()中等待的信号必须在sigwait()之外被屏蔽(屏蔽),以确保在任何其他时间都不会传送。{/ p>

鉴于您的调度循环正在等待传入消息,因此很容易在mq_receive()中阻塞。但是,当看到“结束”消息的工作线程发出信号时,我看不出你是如何避免信号竞争的。或许,您可以mq_send()向调度循环发送特殊的“STOP”消息 - 可能是通过设置POOL_TERMINATING标志并发送空消息?

您提到使用条件变量发送信号以将消息分派给工作线程。这意味着您有一些内部队列,受条件的互斥锁保护。如果是这种情况,那么您也可以将POOL_TERMINATING标记置于互斥锁下,并使用pthread_cond_broadcast()唤醒所有线程。

如果让工作线程为自己做mq_receive() - 这看起来似乎有道理 - 那么你有一个小问题就是向所有人发送信号以终止。在这种情况下,主线程将等待被告知已收到终止消息。在通过pthread_join()收集工作人员(我假设)之前,如果在pthread_kill()中被阻止,则需要mq_receive()每个工作人员。然而,我不清楚你是如何避免信号竞赛......这开始看起来非常讨厌。这可能是pthread_cancel()有用的(少数恕我直言)时间之一...您可以设置工作人员PTHREAD_CANCEL_DISABLEPTHREAD_CANCEL_DEFERRED,并在{PTHREAD_CANCEL_ENABLE之前设置mq_receive() 1}}。或者......与上述类似,您可以为所有工作人员mq_send()“停止”消息。

顺便说一句,我认为我们需要的是pthread_prod(),它是pthread_kill()pthread_cancel()之间的交叉......一种信号,除了在(期间)之外被屏蔽取消点。这将概括pselect()通过其神奇的“原子”改组信号掩码所做的事情。

答案 1 :(得分:0)

所有这一点都归功于alk,他让我三思而后行 使用信号(以及signal(7)中的信号,因为我使用过 pthread_cond_signal(3))。最后,我甚至设法使用非阻塞 队列,不用担心错过新消息通知。

一些伪代码:(最终的代码是功能性的,但它......并不完全适合 用于教育目的)。

DISPATCHER:

  mq_open();
  mq_notify(dispatcher_callback);

  while (state != TERMINATING)

    switch (state) 

      case WAITING:

        while (dispatcher_event == NO_EVENT)
          wait(dispatcher_event);

        state = (dispatcher_event == NEW_MSG) ? RECEIVING :
                (dispatcher_event == TERM)    ? TERMINATING : WAITING;

      case RECEIVING:

        if (mq_receive() > -1)
          signal(worker_event, NEW_MSG);
          forward_command();

        else if (errno == EAGAIN)
          state = WAITING;
          dispatcher_event = NO_EVENT;

  worker_event = TERM;
  broadcast(worker_event, TERM);

  for (w ∈ workers)
    pthread_join(w.id);

  return;


WORKER:

  while (state != TERMINATING)

    switch (state)

      case IDLE:

        while (worker_event == NO_EVENT)
          wait(worker_event);

        state = (worker_event == NEW_MSG) ? BUSY :
                (worker_event == TERM)    ? TERMINATING : IDLE;

      case BUSY:

        worker_event = NO_EVENT;
        worker_fetch_command();
        worker_process_command();
        state = IDLE;

  return;

“事件”对象由条件变量,互斥体和 枚举类型(因此signal() / broadcast() / wait()调用,混合使用 作业和比较)。 while (!predicate_change) wait(predicate); 成语是为了处理虚假的唤醒。

调度程序和工作线程共享一个包含的公共“池”对象 两个事件(一个用于调度程序,一个用于整个工作池)和一个缓冲区 拿起命令的地方。

没有图示:

  • 之前和之后适当的lock() / unlock()来电 读/写/发信号/等待这些“事件”。
  • dispatcher_forward()和之间的更多同步 worker_fetch_command()
  • 这些“状态”变量是线程本地的。
  • dispatcher_callback()再次致电mq_notify(),然后发出信号dispatcher_event,并将其设为NEW_MSG
  • process_command()在识别出来时发出dispatcher_event信号 验证“TERMINATE”命令。
  • 检查错误。
  • ......一些printf()来电。

编辑:在考虑了一些之后,我想我可以完全没有调度员线程:

  • 工人可以不受阻挡地致电mq_receive();
  • 通知回调可以直接向池发送/广播事件;
  • 识别终止命令的工作人员也可以将事件广播到池中,而不是反弹回调度员。