posix管道作为工作队列

Mar*_*ork 5 c++ pipe message-queue

我见过的工作队列的正常实现涉及互斥和条件变量.

消费者:

A) Acquires Lock
B) While Queue empty
      Wait on Condition Variable (thus suspending thread and releasing lock)
C) Work object retrieved from queue
D) Lock is released
E) Do Work
F) GOTO A
Run Code Online (Sandbox Code Playgroud)

制片人:

A) Acquires Lock
B) Work is added to queue
C) condition variable is signaled (potentially releasing worker)
D) Lock is released
Run Code Online (Sandbox Code Playgroud)

我一直在浏览一些代码,我看到了一个使用POSIX管道的实现(我以前没见过这种技术).

消费者:

A) Do select on pipe (thus suspending thread while no work)
B) Get Job from pipe
C) Do Work
D) GOTO A
Run Code Online (Sandbox Code Playgroud)

制片人:

A) Write Job to pipe.
Run Code Online (Sandbox Code Playgroud)

由于生产者和消费者是同一个应用程序内的线程(因此它们共享相同的地址空间,因此它们之间的指针是有效的); 作业将作为工作对象(A C++对象)的地址写入管道.因此,必须从管道写入/读取的所有内容都是一个8字节的地址.

我的问题是:

  • 这是一种常见的技术(我是否受到了保护?)有哪些优点/缺点?

我的好奇心被激发了,因为管道技术不涉及任何可见的锁定或信号(它可能隐藏在选择中).所以我想知道这是否会更有效率?

编辑:

根据@Maxim Yegorushkin的评论回答.

实际上,这个场景中的"Producer"涉及来自大量并行源的大量IO.所以我怀疑原作者虽然非常希望这个帖子在任何情况下都不会阻塞,但也不想在"Producer"线程中高成本工作.

Max*_*kin 5

正如这里已经提到的,人们使用管道作为队列来避免在非阻塞 I/O 线程(即处理多个套接字并阻塞select/的线程)中的条件变量上阻塞epoll。如果 I/O 线程在条件变量或互斥锁上阻塞,它就不能再进行非阻塞 I/O。

有人说写入管道涉及系统调用,并且当线程间事件量很高时可能会增加延迟。这仅适用于基于管道的简单队列实现。

高级实现使用无锁的作业/事件链表,并且只有当第一个作业被添加到列表时,管道才会被写入以从阻塞epoll调用中唤醒目标 I/O 线程(基本上使用管道作为边缘触发的通知机制,但不是用于传递指向作业/事件的指针)。因为唤醒线程需要几微秒的时间,所以在此期间可能会有更多的作业/事件发布到该线程的事件队列,但每个后续事件都不需要写入管道,直到稍后 I/O线程唤醒并消耗队列中的所有事件。此外,在较新的 Linux 内核中,eventfd可以使用更快的速度代替管道来唤醒 I/O 线程。