I have a question about the notify_one function. At the following code,
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
#include <mutex>
std::condition_variable cv;
std::mutex m;
bool ready = false;
void f()
{
std::unique_lock<std::mutex> lk(m);
std::cout << "get into wait, ready=" << ready << std::endl;
cv.wait(lk, []() { return ready; });
std::cout << "get out of wait, ready=" << ready << std::endl;
}
int main()
{
std::thread a(f);
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::unique_lock<std::mutex> lk(m, std::defer_lock);
if (lk.try_lock()) {
std::cout << "main try_lock success, ready=" << ready << std::endl;
ready = true;
}
}
std::cout << "main notify, ready=" << ready << std::endl;
cv.notify_one();
// std::cout << "hello" << std::endl;
{
std::unique_lock<std::mutex> lk(m, std::defer_lock);
if (lk.try_lock()) {
std::cout << "main try_lock success, ready=" << ready << std::endl;
ready = true;
}
}
std::cout << "main notify, ready=" << ready << std::endl;
cv.notify_one();
a.join();
return 0;
}
I get a following result,
get into wait, ready=0
main try_lock success, ready=0
main notify, ready=1
main try_lock success, ready=1
main notify, ready=1
get out of wait, ready=1
But I expect a below result, because, according to the page, if notify_one is called wait is unblocked and lock of mutex(m) is reacquired.
get into wait, ready=0
main try_lock success, ready=0
main notify, ready=1
main notify, ready=1
get out of wait, ready=1
I found if I comment out the std::cout << "hello" << std::endl; I get expected result. Looks to me like notify_one does not immediately unblock the wait. It is correct?
Thank you very much!
Looks to me like notify_one does not immediately unblock the wait. It is correct?
Notification immediately unblocks the wait, meaning the wait is able to resume after the notification. That is, the sleeping thread is marked as runnable.
However, the scheduler doesn't necessarily re-start it immediately. If it would have to pre-empt some already running thread/process, it will probably wait until a yield, a syscall or some other cancellation point, and otherwise not even look at the newly-runnable thread until the end of the current timeslice.
Unblocking something is not the same as forcing an immediate context switch (which is lucky, as otherwise synchronization would be even more expensive).
After your first cv.notify_one(); you have no synchronization points (until a.join();) and the thread already running will most likely continue to run and reach a.join(); before the scheduler decides to give the other thread a spin.
As an experiment, you can yield the thread's execution slot by sleeping a little after notifying - and it may cause the order of the output to be what you expected.
cv.notify_one();
std::this_thread::sleep_for(std::chrono::milliseconds(1));
Don't rely on it in production code though. This doesn't guarantee anything since there is no synchronization between the threads (other than the join()).
Related
Is it clear what would happen if a thread blocked on a std::condition_variable receives a notification but the lock on the associated mutex is not yet released, and the lock will be released 10 seconds later? Would the thread wait for the lock to be released or is the situation undefined?
I added a 15seconds sleep on purpose to make the lock unavailable during that time to see how waiting thread will be doing. It is doing OK but just wanted to be sure about it.
#include <iostream>
#include <condition_variable>
#include <thread>
#include <chrono>
struct SharedResource
{
SharedResource() :
cv_mutex(), cv(), counter(0)
{
}
/*
This mutex is used for three purposes:
1) to synchronize accesses to counter
2) to synchronize accesses to std::cerr
3) for the condition variable cv
*/
std::mutex cv_mutex;
std::condition_variable cv;
int counter;
};
void waits(SharedResource& sharedRes)
{
std::unique_lock<std::mutex> lk(sharedRes.cv_mutex);
std::cerr << "Waiting... \n";
while (sharedRes.counter != 1)
{
sharedRes.cv.wait_for(lk,3s);
std::cerr << "Thread ID: " << std::this_thread::get_id() << " wakes up every 3 seconds.\n";
}
std::cerr << "...finished waiting." << "counter: " << sharedRes.counter << std::endl;
} //The lk object will be unlocked after this scope ends.
void signals(SharedResource& sharedRes)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
{
std::lock_guard<std::mutex> lk(sharedRes.cv_mutex);
std::cerr << "Notifying...\n";
} // The lk object will be unlocked after this scope ends.
sharedRes.cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(6));
{
std::lock_guard<std::mutex> lk(sharedRes.cv_mutex);
sharedRes.counter = 1;
std::cerr << "Notifying again...\n";
sharedRes.cv.notify_all();
std::this_thread::sleep_for(std::chrono::seconds(15));
}// The lk object will be unlocked after this scope ends.
}
int main()
{
SharedResource sharedRes;
std::thread
t1(waits, std::ref(sharedRes)),
t2(signals, std::ref(sharedRes));
t1.join();
t2.join();
}
If a thread that is blocked on a std::condition_variable is notified, but the lock on the associated mutex has not been released what would be result?
It will continue to wait / wait_for until it can reacquire the lock.
When std::condition_variable::wait and wait_for returns (for whatever reason), the lock is held again, so you don't have to worry about that.
It can even return from wait without having gotten any notifications (spurious wake-ups) - but, no matter what, the lock is reacquired when the call returns.
I got code like below:
std::mutex mutex;
std::condition_variable condition_variable;
bool finish = false;
void test() {
while (true) {
std::unique_lock<std::mutex> lock(mutex);
condition_variable.wait(lock);
if (finish){
std::cout << "finish detected" << std::endl;
return;
}
}
}
int main() {
std::thread t(test);
std::unique_lock<std::mutex> lock(mutex);
finish = true;
lock.unlock();
//sleep(1);
condition_variable.notify_all();
std::cout << "notify_all" << std::endl;
t.join();
}
and the code will not terminate when running, the notify_all log will print, but the finish detected log will not. If I use debug mode, the code will terminate successfully, so I cannot provide a clear clue about the status of the running code, but if I release the sleep(1), the code will works.
So can anyone help what's wrong with my code?
Condition variables have no state, so that when you signal it and there are no waiters the signal is lost. It happens in your code when condition_variable.notify_all() executes before condition_variable.wait(lock);.
The code doesn't use correct method to wait on the condition variable. The correct method is:
Lock the mutex.
Check the condition (finish here).
If the condition is not satisfied, wait on the condition variable. The condition variable can be woken up spuriously. Goto 2.
Fix:
void test() {
std::unique_lock<std::mutex> lock(mutex);
while(!finish)
condition_variable.wait(lock);
std::cout << "finish detected" << std::endl;
}
There is another overload of condition_variable::wait that does the while loop for you:
void test() {
std::unique_lock<std::mutex> lock(mutex);
condition_variable.wait(lock, [&finish]{ return finish; });
std::cout << "finish detected" << std::endl;
}
In the following codes I suppose mLooperMutex can't be acquired by the children thread. But the program output is quite surprising. It looks that mLooperMutex captured in the std::thread is not the same one in main thread.
But if I changed the detach() call of std::thread to join(), this will lead to a deadlock since the mLooperMutex has been locked by the main thread.
Are there anything wrong with this program if I'd like to use the mLooperMutex among different threads?
a.out:
main: wait cond begin
child: acquiring lock begin
child: acquiring lock done
child: notify one begin
child: notify one done
main: wait cond done
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
using namespace std;
int main()
{
std::condition_variable looperSet;
bool child_done = false;
std::mutex mLooperMutex;
cout << "main: acquiring lock begin" << endl;
std::unique_lock<std::mutex> lock(mLooperMutex);
cout << "main: acquiring lock done" << endl;
std::thread{[&mutex=mLooperMutex, &looperSet, &child_done] () {
cout << "child: acquiring lock begin" << endl;
std::unique_lock<std::mutex> lock(mutex);
cout << "child: acquiring lock done" << endl;
child_done = true;
lock.unlock();
cout << "child: notify one begin" << endl;
looperSet.notify_one();
cout << "child: notify one done" << endl;
}}.detach();
cout << "main: wait cond begin" << endl;
looperSet.wait(lock, [&child_done]{ return child_done; });
cout << "main: wait cond done" << endl;
return 0;
}
The reason why the mLooperMutex can be acquired in the child thread is because the lock is released by looperSet.wait:
// This unlocks "lock", and then locks it again afterwards.
looperSet.wait(lock, [&child_done]{ return child_done; });
The reason why this doesn't work with .join() is because .join() waits for the thread to finish before proceeding, and the thread can't finish until the lock is released, and looperSet.wait() which releases the lock won't run until .join() finishes.
Creating a thread and then immediately calling .join() is not very useful, you might as well run the code directly rather than using a thread.
I'm trying to run a thread with a function from a class member and use conditional variable to wait until the main thread signals and add the times the thread got signaled. Here is the code:
// Example program
#include <iostream>
#include <string>
#include <atomic>
#include <thread>
#include <unistd.h>
#include <mutex>
#include <condition_variable>
std::mutex m_mutex;
std::condition_variable m_condVar;
char stop =0;
class dummclass
{
std::thread dummclass_thread;
int alarms;
public:
dummclass() :
alarms(0),
dummclass_thread(std::thread(&dummclass::dummclassThreadProc, this))
{
}
~dummclass()
{
std::cout<<"Alarms: "<<alarms<<"\n";
//signal thread before joining
{
std::lock_guard<std::mutex> lock_guard(m_mutex);
stop=1;
}
m_condVar.notify_one();
dummclass_thread.join();
}
private:
void dummclassThreadProc()
{
{
std::unique_lock<std::mutex> mlock(m_mutex);
std::cout<<"thread waiting\n";
m_condVar.wait(mlock);
std::cout<<"thread done waiting\n";
}
sleep(1);
std::unique_lock<std::mutex> mlock(m_mutex);
while (!stop)//!stop_dummclass.load())
{
std::cout<<"got mutex\n";
m_condVar.wait(mlock);
std::cout<<"wait done\n";
{
std::cout<<"got one\n";
alarms++;
}
}
std::cout<<"end loop\n";
}
};
int main()
{
dummclass *x = new dummclass;
sleep(3);
{
std::lock_guard<std::mutex> lock_guard(m_mutex);
}
m_condVar.notify_one();
std::cout<<"done waiting\n";
sleep(3);
for(int i=0;i<13;i++)
{
{
std::cout<<"signal "<<i<<"\n";
std::lock_guard<std::mutex> lock_guard(m_mutex);
}
m_condVar.notify_one();
}
delete x;
}
The weird part is that the initial waiting and signaling that are outside of the loops actually work ok. I don't understand what mistake I do so that the while loop inside the class thread doesn't catch any signal from the main thread but it catches a signal from the destructor of the dummyclass when I delete it. This is the output:
thread waiting
done waiting
thread done waiting
got mutex
signal 0 signal 1 signal 2 signal 3 signal 4 signal 5 signal 6 signal 7 signal
8 signal 9 signal 10 signal 11 signal 12
Alarms: 0
wait done
got one end loop
EDIT: It seems that adding a 1 second sleep in the main() for loop solves the problem. Is it possible that the for loop gets the mutex before wait() manages to wake and lock the mutex ?
for(int i=0;i<13;i++)
{
{std::cout<<"signal "<<i<<"\n";
std::lock_guard<std::mutex> lock_guard(m_mutex);}
m_condVar.notify_one();
sleep(1);
}
Can someone please show me what is wrong ?
Thanks.
The object doing the waiting gets deleted before it processes the signal. Since the delete happens on a known to be running thread it has a fair chance to get executed first. In particular it is also likely to reacquire the lock again: Since the notify_one() is done while the mutex is locked the wait()ing thread cannot acquire it and will go back to sleep, waiting for the mutex to be released. That gives the signalling thread an opportunity to reacquire the lock. The only forced synchronizqtion causing the signalling thread to wait is the join() and it does give the waiting thread a chance to execute.
Note that signals of condition variables are not something delivered to the waiting thread. They are essentially wake-up calls. The waiting thread will wake up eventually once a signal is delivered. However, many signals can be delivered before it actually does so.
I don't understand what mistake I do so that the while loop inside the
class thread doesn't catch any signal from the main thread
Even though multiple notifications are sent the thread may only receive a single notification.
The notify_one() call does
not mean that the current thread will stop and wait for another thread.
It just means that the other thread must wake up at some point because something may have happened that it would be interested in.
Also note that std::condition_variable::wait could experience a spurious wakeup, so it might not even have anything to do or have received a 'real' signal.
The solution is to provide a predicate as a parameter to the wait() call. The predicate can then check if there is a signal (via a variable provided for this purpose and only changed under lock) and may also check if the program has been stopped.
In the updated program below I've added a predicate to the wait and made some minor changes. The program only notifies under lock, but you might choose not to.
// Example program - modified
#include <iostream>
#include <string>
#include <atomic>
#include <thread>
//#include <unistd.h>
#include <mutex>
#include <condition_variable>
#include <chrono>
std::mutex m_mutex;
std::condition_variable m_condVar;
bool signal_waiting{false};
bool stop{false};
class dummclass
{
int alarms{};
std::thread dummclass_thread{[this](){dummclassThreadProc(); }};
public:
~dummclass()
{
std::cout << "Alarms: " << alarms << "\n";
//signal thread before joining
{
std::lock_guard<std::mutex> lock_guard(m_mutex);
stop = 1;
m_condVar.notify_one();
}
dummclass_thread.join();
}
private:
void dummclassThreadProc()
{
{
std::unique_lock<std::mutex> mlock(m_mutex);
std::cout << "thread waiting\n";
m_condVar.wait(mlock);
std::cout << "thread done waiting\n";
}
std::this_thread::sleep_for(std::chrono::seconds{1});
while(!stop)//!stop_dummclass.load())
{
std::unique_lock<std::mutex> mlock(m_mutex);
std::cout << "got mutex\n";
//m_condVar.wait(mlock);
m_condVar.wait(mlock, [](){return signal_waiting || stop; });
if(stop)
break;
std::cout << "wait done\n";
std::cout << "got one\n";
alarms++;
signal_waiting = false;
m_condVar.notify_one();
}
std::cout << "end loop\n";
}
};
int main()
{
dummclass *x = new dummclass;
//sleep(3);
std::this_thread::sleep_for(std::chrono::seconds{1});
{
std::lock_guard<std::mutex> lock_guard(m_mutex);
m_condVar.notify_one();
}
std::cout << "done waiting\n";
//sleep(3);
std::this_thread::sleep_for(std::chrono::seconds{1});
for(int i = 0; i<13; i++)
{
{
std::cout << "signal " << i << "\n";
std::unique_lock<std::mutex> lock(m_mutex);
m_condVar.wait(lock, [](){return !signal_waiting ; });
signal_waiting = true;
m_condVar.notify_one();
}
}
delete x;
}
Typical usages of condition variables look like this (see code below): http://en.cppreference.com/w/cpp/thread/condition_variable.
However, it seems that the main thread could potentially call notify_one before the worker thread calls wait, which would result in a deadlock. Am I mistaken? If not, what is the usual workaround for this?
#include <iostream>
#include <string>
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex m;
std::condition_variable cv;
std::string data;
bool ready = false;
bool processed = false;
void worker_thread()
{
// Wait until main() sends data
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return ready;});
// after the wait, we own the lock.
std::cout << "Worker thread is processing data\n";
data += " after processing";
// Send data back to main()
processed = true;
std::cout << "Worker thread signals data processing completed\n";
// Manual unlocking is done before notifying, to avoid waking up
// the waiting thread only to block again (see notify_one for details)
lk.unlock();
cv.notify_one();
}
int main()
{
std::thread worker(worker_thread);
data = "Example data";
// send data to the worker thread
{
std::lock_guard<std::mutex> lk(m);
ready = true;
std::cout << "main() signals data ready for processing\n";
}
cv.notify_one();
// wait for the worker
{
std::unique_lock<std::mutex> lk(m);
cv.wait(lk, []{return processed;});
}
std::cout << "Back in main(), data = " << data << '\n';
worker.join();
}
Note the definition of wait that uses a condition (the only wait you should ever be using):
while (!pred()) {
wait(lock);
}
if notify was already fired it means the condition is already true (that was sequenced before notify_one in the signalling thread). So when the receiver takes the mutex and looks at pred(), it will be true and it will proceed.