我的任务是修改同步C程序,使其可以并行运行。我们的目标是让它尽可能便携,因为它是许多人使用的开源程序。因此,我认为最好将程序包装在C ++层中,这样我就可以利用便携式的boost库。我已经完成了这一切,一切似乎都按预期工作。
我遇到的问题是决定在线程之间传递消息的最佳方法是什么。幸运的是,该程序的体系结构是多生产者和单个消费者的体系结构。更好的是,消息的顺序并不重要。我已经读过单一生产者/单一消费者(SPSC)队列将受益于这种架构。那些有多线程编程经验的人有什么建议吗?我对这些东西很新。此外,我们将非常感谢任何使用boost实现SPSC的代码示例。
答案 0 :(得分:7)
以下是我用于合作多任务/多线程库(MACE)http://bytemaster.github.com/mace/的技术。除队列为空之外,它具有无锁的优点。
struct task {
boost::function<void()> func;
task* next;
};
boost::mutex task_ready_mutex;
boost::condition_variable task_ready;
boost::atomic<task*> task_in_queue;
// this can be called from any thread
void thread::post_task( task* t ) {
// atomically post the task to the queue.
task* stale_head = task_in_queue.load(boost::memory_order_relaxed);
do { t->next = stale_head;
} while( !task_in_queue.compare_exchange_weak( stale_head, t, boost::memory_order_release ) );
// Because only one thread can post the 'first task', only that thread will attempt
// to aquire the lock and therefore there should be no contention on this lock except
// when *this thread is about to block on a wait condition.
if( !stale_head ) {
boost::unique_lock<boost::mutex> lock(task_ready_mutex);
task_ready.notify_one();
}
}
// this is the consumer thread.
void process_tasks() {
while( !done ) {
// this will atomically pop everything that has been posted so far.
pending = task_in_queue.exchange(0,boost::memory_order_consume);
// pending is a linked list in 'reverse post order', so process them
// from tail to head if you want to maintain order.
if( !pending ) { // lock scope
boost::unique_lock<boost::mutex> lock(task_ready_mutex);
// check one last time while holding the lock before blocking.
if( !task_in_queue ) task_ready.wait( lock );
}
}
答案 1 :(得分:1)
如果只有一个消费者而是多个生产者,那么我会使用一个数组或类似数组的数据结构,其中O(1)访问时间,其中每个数组槽代表一个生产者 - 消费者队列。单生产者 - 消费者队列的巨大优势在于,您可以在没有任何显式同步机制的情况下使其无锁,从而使其成为多线程环境中非常快速的数据结构。有关单生产者 - 消费者队列的简单实现,请参阅my answer here。
答案 2 :(得分:1)
网上有许多生产者 - 消费者队列的例子,对多个生产者/消费者来说是安全的。 @bytemaster发布了一个在每个消息中使用链接来消除队列类本身的存储 - 这是一个很好的方法,我自己在嵌入式作业中使用它。
如果队列类必须提供存储,我通常使用大小为N的“池队列”,在启动时加载N *消息类实例。需要通信的线程必须从池中弹出*消息,加载它并将其排队。当最终'用完'时,*消息被推回到池中。这会限制消息的数量,因此所有队列只需要长度为N - 没有调整大小,没有新的(),没有删除(),容易泄漏检测。