Possible deadlock in C++/boost/thread
Suppose the following code is run on single-core processor:
#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;
}
and processor stopped after 1.1 (where size = 0) within Reader thread and switched to Writer where index was added (2.1) into the buffer and bufferHasSome was notified (at 2.2) (but no one is waiting for it yet so it was just void operation); then processor switched back to Reader thread and started (at 1.2) to wait while somebody will write something开发者_StackOverflow社区 to the buffer but only one who can write is waiting for somebody to read the buffer. This program freeze after averagely 150 iterations - I think it's because of this. What did I missed? How to fix it?
I see a couple problems here. Most importantly, you're checking shared values (namely, buffer.size()) outside of a lock. Secondly, you have these weird mutexes local to each function which do absolutely nothing, since they're not shared between threads. If you lock the bufferMutex before checking buffer.size(), then wait on bufferMutex (meaning you'll unlock it, which is correct, then re-lock it when the thread is notified), I think the deadlock threat should be gone.
Presumably your writer is waiting for the reader to have finished reading before it writes again.
In any case you need just the one "global" mutex and should use that when waiting on your condition variables too.
The local mutexes have no effect.
I would also suggest that your main function join your two threads (I would use thread_group). You will also need some terminating condition for your loop. Maybe the writer will set a variable when it has completed and broadcast, and your reader thread will check that condition as well as checking the state of the queue.
Not quite an answer to your question. The others seem to have alrdy started with that. I would like to suggest that you take a look at concurrent_bounded_queue in TBB. If you use that your code will be simplified and less error-prone.
#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;
}
Your problem might have something to do with this reading loop:
for(int i = 0; i < buffer.size(); i++)
{
printf("%d\n", buffer.front());
buffer.pop_front();
}
The problem is that the above will only read half the elements. Because as your are popping elements, the buffer.size() is decreasing, so the iterations are going to end when the size is half of what it was when it started. You should just replace it by a while loop:
while(buffer.size() > 0)
{
printf("%d\n", buffer.front());
buffer.pop_front();
}
Basically, what was happening is that for a while it gets lucky and kinda works (in parts due to spurious wake-ups of the condition variables), but eventually, the reader thread just never really clears the buffer and the writer thread never wakes up. At least, I think that is the problem... multi-threading issues are never trivial to see at first glance.
精彩评论