我需要一个队列来将消息从一个线程(A)传递到另一个线程(B),但是我没能找到一个真正做到我想要的东西,因为它们通常允许添加一个项目失败,一个案例在我的情况下非常致命,因为需要处理消息,并且线程真的无法停止并等待空余空间。
答案 0 :(得分:7)
以下是如何在C ++中编写无锁队列:
http://www.ddj.com/hpc-high-performance-computing/210604448
但是当你说“线程A必须阻止”时,你确定这是要求吗? Windows不是实时操作系统(在正常使用中也不是linux)。如果您希望线程A能够使用所有可用的系统内存,那么它需要分配内存(或等待其他人执行)。如果读取器和写入器都采用进程内锁(即非共享互斥锁)来操作列表,则操作系统本身不能提供比您所拥有的更好的时序保证。添加消息的最坏情况是必须转到操作系统才能获得内存。
简而言之,您不喜欢的队列有一个固定容量的原因 - 这样他们就不必在所谓的低延迟线程中分配内存。
因此,无锁代码通常会减少阻塞,但由于内存分配不能保证,并且使用互斥锁的性能不应该是那么糟糕,除非你有一个真正庞大的流要处理的事件(例如,您正在编写网络驱动程序,而消息是传入的以太网数据包)。
所以,在伪代码中,我要尝试的第一件事就是:
Writer:
allocate message and fill it in
acquire lock
append node to intrusive list
signal condition variable
release lock
Reader:
for(;;)
acquire lock
for(;;)
if there's a node
remove it
break
else
wait on condition variable
endif
endfor
release lock
process message
free message
endfor
只有当这证明在编写器线程中引入了不可接受的延迟时,我才能使用无锁代码(除非我碰巧有一个合适的队列已经存在)。
答案 1 :(得分:1)
Visual Studio 2010正在添加2个支持此场景的新库,Asynchronous Agents Library和并行模式库。
代理库具有支持或异步消息传递,并包含用于向“目标”发送消息和从“源”接收消息的消息块
unbounded_buffer是一个模板类,它提供了我认为你正在寻找的东西:
#include <agents.h>
#include <ppl.h>
#include <iostream>
using namespace ::Concurrency;
using namespace ::std;
int main()
{
//to hold our messages, the buffer is unbounded...
unbounded_buffer<int> buf1;
task_group tasks;
//thread 1 sends messages to the unbounded_buffer
//without blocking
tasks.run([&buf1](){
for(int i = 0 ; i < 10000; ++i)
send(&buf1,i)
//signal exit
send(&buf1,-1);
});
//thread 2 receives messages and blocks if there are none
tasks.run([&buf1](){
int result;
while(result = receive(&buf1)!=-1)
{
cout << "I got a " << result << endl;
}
});
//wait for the threads to end
tasks.wait();
}
答案 2 :(得分:1)
为什么不使用STL&lt; list
&gt;或&lt; deque
&gt;用互斥量添加/删除? thread-safety of STL不足吗?
为什么不创建包含指针的自己的(单/双)链表节点类,并且要添加/删除的项继承自该指针?因此不需要额外的分配。您只需在threadA::add()
和threadB::remove()
中点击几个指针就可以了。 (虽然你想在互斥体下做到这一点,但是除非你做了一些非常错误的事情,否则对threadA的阻塞效果可以忽略不计......)
如果您使用的是pthread,请查看sem_post()
和sem_wait()
。我们的想法是,threadB可以通过sem_wait()
无限期地阻塞,直到threadA将某些东西放入队列。然后threadA调用sem_post()
。这唤醒了threadB做它的工作。之后threadB可以回去睡觉。这是处理异步信令的有效方式,在threadA::add()
完成之前支持多个threadB::remove()
之类的内容。
答案 3 :(得分:0)
您可能想要考虑您的要求 - A是否真的无法丢弃任何队列项?或者你不希望B从队列中拉出两个连续的元素,这些元素不是连续的项目,因为这会以某种方式歪曲一系列事件?
例如,如果这是某种数据记录系统,你(可以理解)不会想要记录中的空白 - 但没有无限的记忆,现实是在某些角落的情况下你可能会超出你的队列容量..
在这种情况下,一种解决方案是拥有某种可以放入队列的特殊元素,这代表A发现它必须丢弃项目的情况。基本上你保留了一个额外的元素,大部分时间都是null。每次A向队列添加元素时,如果这个额外的元素不为空,那就进入。如果A发现队列中没有空间,那么它将这个额外的元素配置成'嘿,队列已满“
这样,A永远不会阻塞,你可以在系统非常繁忙时删除元素,但你不会忽略元素被删除的事实,因为只要队列空间可用,这个标记就会进入指出数据丢失的位置。当进程B发现它已将此超出标记元素从队列中拉出时,它会执行它需要做的任何事情。