I have following code
#include <thread>
#include <mutex>
#include <condition_variable>
#include <iostream>
#include <set>
#include <string>
#include <chrono>
#include <atomic>
using namespace std;
set<string> messages_;
mutex mu;
void thread1() {
for(int i=0; i<20; i) {
{
lock_guard<mutex> lock(mu);
messages_.insert(to_string(i));
}
this_thread::sleep_for(chrono::milliseconds(20));
}
}
condition_variable cond;
mutex mutex_;
atomic<bool> stop{false};
int workaround = 0;
mutex* GetCoutMutex() {
static std::mutex mu;
return μ
}
void Work() {
workaround ;
unique_lock<mutex> lock(mu);
for(auto it=messages_.begin(); it!=messages_.end(); ) {
lock.unlock();
{
lock_guard<mutex> cout_lock(*GetCoutMutex());
cout << "work around: " << workaround << ", message: " << *it << endl;
}
lock.lock();
it=messages_.erase(it);
}
}
void thread2() {
while(!stop.load(memory_order_acquire)) {
Work();
unique_lock<mutex> lock(mutex_);
cond.wait_for(lock, chrono::milliseconds(50), []{ return stop.load(memory_order_acquire); });
}
}
int main() {
thread t1(thread1), t2(thread2);
t1.join();
this_thread::sleep_for(chrono::milliseconds(100));
stop.store(true, memory_order_release);
cond.notify_one();
t2.join();
}
My expected output is
work around: 1, message: 0
work around: 2, message: 1
work around: 2, message: 2
work around: 3, message: 3
work around: 3, message: 4
work around: 4, message: 5
work around: 4, message: 6
work around: 4, message: 7
work around: 5, message: 8
work around: 5, message: 9
work around: 6, message: 10
work around: 6, message: 11
work around: 7, message: 12
work around: 7, message: 13
work around: 7, message: 14
work around: 8, message: 15
work around: 8, message: 16
work around: 9, message: 17
work around: 9, message: 18
work around: 9, message: 19
in which, the message number is a increasing sequence 0,1,2,...,19 and most executions meet the expection. However, I got following output in some execution:
work around: 1, message: 0
work around: 2, message: 1
work around: 2, message: 2
work around: 3, message: 3
work around: 3, message: 4
work around: 4, message: 5
work around: 4, message: 6
work around: 5, message: 7
work around: 5, message: 8
work around: 6, message: 10
work around: 6, message: 11
work around: 6, message: 9
work around: 7, message: 12
work around: 7, message: 13
work around: 8, message: 14
work around: 8, message: 15
work around: 9, message: 16
work around: 9, message: 17
work around: 9, message: 18
work around: 10, message: 19
As far as I know, thread1 and thread2 are synchronized with mu
, so the insertion of thread1 should be visible to thread2. As thread1 insert 9 before 10, and thread2 is iterating the set in order, so 9 should be output first by thread2.
I'm confused. Can someone explain why 10 is output before 9?
CodePudding user response:
Since the set contains std::string
, the elements are sorted in lexicographic order.
Suppose Thread 1 gets to insert strings "9", "10" and "11" while Thread 2 is waiting on condvar. In this case, "10" will be the smallest string and thus it will be printed first followed by "11" and "9".