Linux 中的 select()、posix 消息队列和多线程

Posted

技术标签:

【中文标题】Linux 中的 select()、posix 消息队列和多线程【英文标题】:select(), posix message queue and multithreading in Linux 【发布时间】:2016-04-24 10:03:56 【问题描述】:

我遇到了关于消息队列的问题:

我已使用 mq_timedreceive() 在 abs_timeout 时间内获取消息队列。 但是这个函数受系统时间(CLOCK_REALTIME)的影响。我的意思是当系统时间发生变化时,abs_timeout(绝对时间)就不再正确了。

为了解决这个问题,我意识到它应该改为 CLOCK_MONOTOIC 时钟。 但是在linux下是没有办法的(我搜了一下发现QNX支持这个机制)。

最后,我将 select() 和 mq_timereceive 与 NO_WAIT 结合起来。 + select():使用相对时间,因此不受系统时间变化的影响。 超时后,我会用mq_timereceive()获取消息队列,当然绝对时间=0;

但我的问题是: 如果系统有很多线程在等待同一个消息队列(使用 select()),

如果一条消息被发送到消息队列,所有等待的线程都被唤醒并运行。所以这是错误的。 可能有一个线程(不是第一个等待线程)首先醒来并收到此消息。 我的预期是只有第一个等待线程应该被唤醒并且它会收到消息,而其他线程仍然阻塞。 请帮忙。

【问题讨论】:

【参考方案1】:

看起来你有几个问题:

    等待消息队列超时,不受时钟调整影响。在 Linux 中,以下 API 支持时钟(CLOCK_REALTIMECLOCK_MONOTONIC 等)选择:timerfd_createtimer_create。将这些与mq_timedreceive 集成的一种方法是让timer_create 触发一个中断mq_timedreceive 的信号。

    将 POSIX 消息队列的等待与 select 集成。最直接的方法是使用mq_notify 使其在有新消息可用时传递信号,从而使select 调用返回-1errno 设置为EINTR

    公平排队,以便第一个服务员收到下一条消息。对于 POSIX 消息队列,如果等待线程在 mq_receive 中被阻塞,则可能会发生这种情况。否则,下一条可用消息将传递到首先调用mq_receive 的线程。

对于同一进程的线程之间的消息传递,另一种方法是让管道充当消息指针队列。也就是说,生产者线程创建一条消息并将指向它的指针写入管道(即不需要序列化整个消息,因为消息接收者在同一个进程中并且可以访问进程地址空间)。任何消费者线程都可以使用select 在管道上等待,然后读取指向消息的指针。但是,如果多个线程在同一个管道上等待,它们都会被唤醒,但只有一个线程会从管道中读取消息指针。

【讨论】:

感谢您的回答。但是您似乎误解了我的问题。我的问题是:带有 abs_time (>0) 的 mq_timedreceive() 受系统时间变化的影响。所以当系统时间改变时它会出错。我通过将 select() 超时和 mq_timedreceive() 与 abs_time = 0 结合来解决此问题。但是当我解决此问题时。我遇到了 3 个新问题(在问题末尾描述)

以上是关于Linux 中的 select()、posix 消息队列和多线程的主要内容,如果未能解决你的问题,请参考以下文章

linux下select函数详解及实例

EPOLL使用详解

epoll的使用

epoll机制和简述

linux 平台上 C# 中的自定义 posix 信号不起作用

_POSIX_TIMERS的可能值是什么?