When using multithreading I often run into the following problem:
I've got an object, say a network receiver(but could be anything). And a function that gets the data. Now sometimes there simply is no data, and you want to let the thread wait to get it's data. A blocking call, very much like being used by Berkeley sockets and it's derived implementations.
The principle is simple:
Well now of course there is other ways of implementing this. But my usual implementation with C++11 is as follows:
Object A
Calls the function in object B on a separate thread, that is dedicated to this task.Object B
Uses an std::condition_variable
construction to block the thread until the data is actually acquired. Object A
Places the data in a queue, which is read out by the main thread.Now my actual problem arises on the destruction of object B
, if it has to be destructed before object A
(returning a nullptr, or something similar on the blocking call). I really wouldn't know how to efficiently wrap up object B
.
The main problem is that object B
isn't aware of the thread, and a thread can only have one handle, which is inside object A
.
Example:
Some code to illustrate my problem.
Say I've got this function in object B:
data* getData
{
std::unique_lock<std::mutex>l_newDataWaiterLock(m_newDataWaiterMutex);
m_newDataWaiter.wait(l_newDataMutex);
if(!running)
return nullptr
else
return data;
}
and this destructor:
~ObjectB()
{
m_running = false;
m_newDataWaiter.notifyAll();
//Point X
}
With these member variables:
std::condition_variable m_newDataWaiter;
std::atomic<bool> m_running;
We still have the problem that the destructor will have to wait at the indicated Point X
, until all the other threads have received the notify, and returned a null.
Now I could cook something up using atomic counters, more std::condition_variable
s and mutexes. But I have a feeling there has to be a more elegant and reliable solution to this problem:). Since this solution would require a notify in every getData
call for the entire lifecycle of object B
.
Note: I'm using C++11, so I've used that to illustrate everything. And I'm hoping to solve it using that. Although this of course this is a more of a general concurrency problem.
How about managing object B
with a std::shared_ptr
and using std::weak_ptr
to store the pointer in A
?
That is slightly more inefficient as each time that A
wants to get access, it temporarily needs to get a std::shared_ptr
through std::weak_ptr::lock()
itself, but you can be sure there is no race condition anymore.