2

I have mailBox class with send and receive methods shared between threads, and threads: thread1 send message, threads 2 and 3 receive messages, how I must using mutex for synchronize this?

Any combinations that I have tried hasn't led to success.

std::mutex g_lock; //in global void sendMessage(Message msg) { if (g_lock.try_lock()) { this_thread::sleep_for(100ms); // DELAY messages->push_back(msg); g_lock.unlock(); } } 

The same for Receive method

full code: https://pastebin.com/7y2RC5br

Also this code can't be debugged because delays change the logic of the code.

Correct logic of the code: thread2/3 try lock and read msg, get empty then unlock thread1 try lock and send msg then unlock thread2/3 try lock and read msg, get msg and write to file then unlock

When I have tried mutex's try_lock from threads 2/3, I had been getting constantly blocked thread and thread 1 was had been working after ALL threads 2/3.

6
  • 1
    You should use a std::lock_guard.` Commented Dec 19, 2020 at 18:42
  • 2
    with try_lock, if lock isn't acquired, you won't send any message. Commented Dec 19, 2020 at 18:42
  • But why lock can't be acquired? Commented Dec 19, 2020 at 18:43
  • You should decide if you can afford NOT locking or not. You need to use lock() instead of try_lock() in certain situations. Commented Dec 19, 2020 at 18:48
  • Re. "But why lock can't be acquired?": presumably because it's already owned by another thread? You really need to provide a minimal reproducible example that demonstrates the problem. Commented Dec 19, 2020 at 18:49

1 Answer 1

5

Box up low level mutexes into building block types.

Lock based thread safety is ridiculously easy to get wrong. Not only is it fragile, it doesn't compose; three routines that are pairwise thread safe can be when combined unsafe.

Thread safety is a relational property, not an absolute one.

There are various known patterns that have been mathematically proven to work. Implemented correctly and you are less doomed. Toss together something, and your code won't be correct.

To design new threading code you will want to get good at proof based computer science. The implementation is easier than designing it correctly.

In your case, I would start with a thread safe queue primitive. You'll need condition variables, mutexes, and a std deque. Then hook each routine up to one end of a queue, and send messages to the other. (The reader should only read from a queue and consume each message in order; the writer, only send messages on a queue).

I mean, this is still going to be hard to get right, but at least your primitives aren't nearly raw mutexes.

template<class T> struct threadsafe_queue { T pop(); void push(T); std::deque<T> pop_all(); std::optional<T> try_pop(); template<class...Ts> std::optional<T> wait_for_pop( std::chrono::duration<Ts...> ); private: mutable std::mutex m; std::condition_variable cv; std::deque<T> queue; std::unique_lock<std::mutex> lock() const; }; 
Sign up to request clarification or add additional context in comments.

1 Comment

I started using unique_lock with condition_variable and it's work much better than only one mutexes.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.