我正在尝试创建一个程序,通过一个公共队列(如它所代表的POSIX消息队列)从各种应用程序接收消息,并将这些命令分派给一个工作线程池。
在过去的几天里,我的设计变得比我记忆中的要多,所以我认为我会来这里寻求建议,特别是如何终止该计划。
我希望通过队列接收终止事件,并由工作人员确认(这样调度员就不会打扰预处理命令,这可能意味着验证检查由工作人员完成)。
因此,我需要一种方法让信息以某种方式从工人回到调度员,告诉后者停止。
在我当前的迭代中,主线程是调度程序线程,从队列接收并与工作者交换信号(通过条件变量)以通知空闲线程挂起的命令。我看到它的方式,信号必须进入图片:
mq_notify
后调用(按顺序)mq_receive
,sigwait
和EAGAIN
; mq_receive
,当信号中断时,EINTR
将返回while(atomic_load(state) != POOL_TERMINATING)
。在这两种情况下,调度程序都将运行if (ret == EAGAIN)
,它将由验证终止命令并发出信号的工作人员设置。
现在提出问题:
sigwait()
和POOL_TERMINATING
之间收到“新消息”通知吗?mq_receive
,发送信号,如果调度员正在接收,EINTR
只返回mq_receive
(我还不知道有什么信号可以告诉系统“中断一个名为{{1}}的调用,但不要打扰处理程序”,所以我猜这将是一个空的处理程序。)提前致谢。
答案 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_DISABLE
和PTHREAD_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()
;