I have two threads trying to lock the same boost::mutex
. One of those threads is continuously processing some data, and the other is periodically displaying the current state. The processing thread, according to my intention, releases the lock very frequently and reacquires it, so that the display thread can tap in and acquire it whenever it needs it. So, obviously, I would like the display thread to acquire the lock next time it's released by the process thread. However, it doesn't do that, instead, it waits for the lock and only acquires it after many lock-release cycles from the process thread.
Please inspect the minimal example illustrating my problem:
#include <boost/thread.hpp>
#include <iostream>
using namespace std;
using namespace boost;
mutex mut;
void process() {
double start = time(0);
while(1) {
unique_lock<mutex> lock(mut);
this_thread::sleep(posix_time::milliseconds(10));
std::cout<<".";
if(time(0)>start+10) break;
}
}
int main() {
thread t(process);
while(!t.timed_join(posix_time::seconds(1))) {
posix_time::ptime mst1 = posix_time::microsec_clock::local_time();
cout<<endl<<"attempting to lock"<<endl;
cout.flush();
unique_lock<mutex> lock(mut);
posix_time::ptime mst2 = posix_time::microsec_clock::local_time();
posix_time::time_duration msdiff = mst2 - mst1;
cout << std::endl<<"acquired lock in: "<<msdiff.total_milliseconds() << endl;
cout.flush();
}
}
Compiled with: g++ mutextest.cpp -lboost_thread -pthread
When I run the executable, a sample output is like this:
...................................................................................................
attempting to lock
....................................................................................................................................................................................................................................................................................................................................................................................................................................
acquired lock in: 4243
...................................................................................................
attempting to lock
........................................................................................................
acquired lock in: 1049
...................................................................................................
attempting to lock
........................................................................................................................
acquired lock in: 1211
....................................
As you can see, in the worst case, the display thread waits for 424 lock-release cycles before it comes round to catching the lock.
I'm obviously using the mutex in a wrong way, but what is the usual way to solve this?
It's not that you're using the mutex wrong, just that threading doesn't do what you expect here. The OS decides which thread runs when (this is known as "scheduling"), and there's nothing in the code that forces a thread switch at the end of the loop; the thread keeps going, and reacquires the lock. One thing to try is to add a call to this_thread::yield()
after releasing the lock (in this case, at the top of the loop, before relocking); that will suggest to the scheduler that it's appropriate for another thread to run. If you really want tightly synchronized interleaving, threads won't do it; instead, write a higher-level function that calls your two functions one after the other.
If the update thread has nothing else to do it can wait for the mutex to become available.
Look at boost::condition_variable. You can read about it here http://www.boost.org/doc/libs/1_53_0/doc/html/thread/synchronization.html and here Using boost condition variables
If having the update thread go to sleep would be a problem -- it is on many GUI systems and you don't specify which one you are using -- consider posting a message from the processing thread to to updating thread. (again the details depend on your platform.) The message may either contain the information necessary to update the display, or be a notification that "now would be a good time to look."
If you do use a condition code, the processing thread should probably yield after signalling the condition and before reacquiring the lock to open up a large window for the updating thread.
You may want to have a look at boost::condition_variable, specially at the methods wait()
and notify_one()
or notify_all()
. The method wait()
will block the current thread until a the lock is released. The methods notify_one()
and notify_all()
notify one or all threads that are waiting for the lock to continue execution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With