假设以下代码在单核处理器上运行:
#include <cstdio>
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include "boost/date_time/posix_time/posix_time.hpp"
#include <deque>
#include <cstdlib>
#include <time.h>
std::deque<int> buffer;
boost::mutex bufferMutex;
boost::condition bufferHasSome;
boost::condition bufferEmpty;
void Reader()
{
boost::mutex mutex;
boost::mutex::scoped_lock lock(mutex);
//read as fast as possible:
while(true)
{
while(buffer.size() <= 0) //1.1
{
bufferHasSome.wait(lock); //1.2
}
bufferMutex.lock();
for(int i = 0; i < buffer.size(); i++)
{
printf("%d\n", buffer.front());
buffer.pop_front();
}
bufferMutex.unlock();
//everything was read:
bufferEmpty.notify_one();
}
}
void Writer()
{
boost::mutex mutex;
boost::mutex::scoped_lock lock(mutex);
int index = 0;
while(true)
{
//write portion:
for(int i = rand() % 5; i >= 0; i--)
{
bufferMutex.lock();
buffer.push_back(index);
bufferMutex.unlock(); //2.1
bufferHasSome.notify_one(); //2.2
index++;
boost::this_thread::sleep(boost::posix_time::milliseconds(rand() % 10));
}
//definetely wait while written portion will be read:
while(buffer.size() > 0)
{
bufferEmpty.wait(lock);
}
}
}
int main()
{
srand(time(NULL));
boost::thread readerThread(Reader);
boost::thread writerThread(Writer);
getchar();
return 0;
}
并且处理器在读取器线程中 1.1 (其中size = 0)后停止并切换到 Writer ,其中添加了索引( 2.1 )进入缓冲区并通知 bufferHasSome ( 2.2 )(但没有人在等待它,所以它是只是无效操作);然后处理器切换回读取器线程并启动( 1.2 )等待某人将某事写入缓冲区但只有一个人可以写正在等待某人阅读缓冲区。 该程序在平均150次迭代后冻结 - 我认为这是因为这个原因。 我错过了什么?如何解决?
答案 0 :(得分:2)
我在这看到几个问题。最重要的是,您正在检查锁外的共享值(即buffer.size())。其次,你有每个函数本地的这些奇怪的互斥体,它们绝对没有任何东西,因为它们不是在线程之间共享的。如果在检查buffer.size()之前锁定bufferMutex,那么等待bufferMutex(意味着你将解锁它,这是正确的,然后在通知线程时重新锁定它),我认为死锁威胁应该消失。
答案 1 :(得分:1)
据推测,你的作家正在等待读者在再次写作之前完成阅读。
在任何情况下,您只需要一个“全局”互斥锁,并且在等待条件变量时也应该使用它。
本地互斥锁无效。
我还建议你的main函数加入你的两个线程(我会使用thread_group)。您还需要一些循环的终止条件。也许作者在完成和广播时会设置一个变量,你的读者线程会检查这个条件以及检查队列的状态。
答案 2 :(得分:1)
对你的问题不是一个完全答案。其他人似乎开始这样做了。我想建议你看看TBB中的concurrent_bounded_queue。如果您使用它,您的代码将被简化并且不易出错。
#include <cstdio>
#include <cstdlib>
#include <time.h>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <tbb/concurrent_queue.h>
tbb::concurrent_bounded_queue buffer;
void Reader()
{
while(true)
{
int value;
buffer.pop(value); // blocks when empty
printf("%d\n", value);
}
}
void Writer()
{
int index = 0;
buffer.set_capacity(5); // If buffer.size() > 5 then push will block.
while(true)
{
for(int i = rand() % 5; i >= 0; i--)
{
buffer.push(index++); // blocks when full
boost::this_thread::sleep(boost::posix_time::milliseconds(rand() % 10));
}
// "definetely wait while written portion will be read."
// Not sure what exactly the purpose of this was. But I guess set_capacity will fulfill the same role.
}
}
int main()
{
srand(time(NULL));
boost::thread readerThread(Reader);
boost::thread writerThread(Writer);
getchar();
return 0;
}
答案 3 :(得分:0)
您的问题可能与此阅读循环有关:</ p>
for(int i = 0; i < buffer.size(); i++)
{
printf("%d\n", buffer.front());
buffer.pop_front();
}
问题是上面只会读取一半的元素。因为当你是弹出元素时,buffer.size()正在减少,所以当大小是它开始时的一半时,迭代将结束。你应该用while循环替换它:
while(buffer.size() > 0)
{
printf("%d\n", buffer.front());
buffer.pop_front();
}
基本上,发生的事情是,有一段时间它变得幸运而且有点工作(部分原因是由于条件变量的虚假唤醒),但最终,读者线程从未真正清除缓冲区和写入程序线程永远不会醒来。至少,我认为这就是问题......多线程问题乍一看从来都不是微不足道的。