Home > OS >  C Set: thread 1 is inserting, is the inserting result visible to iterator in thread 2?
C Set: thread 1 is inserting, is the inserting result visible to iterator in thread 2?

Time:05-08

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 &mu;
}

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".

  • Related