0

I know I should use std::scoped_lock to lock two mutexes at the same time. But I don't why.

I've read this post std::lock_guard or std::scoped_lock?. But I don't find anyone explains why there would a deadlock in the std::lock_guard version.

#include <mutex>
#include <thread>

std::mutex mtx1;
std::mutex mtx2;

auto main(int argc, char* argv[]) -> int {
  std::mutex mtx1;
  std::mutex mtx2;

  const auto thread_func = [&] {
    std::scoped_lock lock(mtx1, mtx2);

    // Why I can't lock like this
    // std::lock_guard<std::mutex> lock1(mtx1);
    // std::lock_guard<std::mutex> lock2(mtx2);
  };

  std::thread th1(thread_func);
  std::thread th2(thread_func);

  th1.join();
  th2.join();

  return 0;
}

Can someone explain this to me? Thank you.

code in godbolt

maidamai
  • 712
  • 9
  • 26
  • 2
    to avoid deadlocks as stated in the [documentation](https://en.cppreference.com/w/cpp/thread/lock) – Alan Birtles Nov 24 '20 at 08:18
  • @AlanBirtles Can you explain why there would be a deadlock? – maidamai Nov 24 '20 at 08:19
  • For example: Thread one has aquired `mtx1` and is waiting for `mtx2`. Thread two has `mtx2` and waiting for `mtx1`. – super Nov 24 '20 at 08:22
  • @super but you still can do them in order in both threads and avoid that? – magmine Nov 24 '20 at 08:25
  • @super Why would that happen? Don't you have to acquire mtx1 before you acquire mtx2? – maidamai Nov 24 '20 at 08:25
  • 4
    Why would you rely on remembering to write them in the correct order when you can use a safe mechanism and avoid the problem completely. In addition, you can imagine scenarios when there are more then 2 mutexes and maybe all threads don't lock all of the mutexes. Then it would get very messy to keep you code correct without `scoped_lock`. – super Nov 24 '20 at 08:31
  • 1
    _"But I don't find anyone explains why there would a deadlock in the `std::lock_guard` version."_ — There would be no deadlock in your code. You even have no threads. If you want to ask such a question, you should better provide a real-world use case. – Daniel Langr Nov 24 '20 at 08:41
  • Note that even after edit, the code does not make any sense. First, your mutexes do not protect anything. Second, you can have only one mutex instead of two. There would be no deadlock with `lock_guard`, but, again, that's not a real-world scenario. – Daniel Langr Nov 24 '20 at 09:01
  • 1
    @super I just want to understand why I should use `std::scoped_lock` instead of `std::lock_guard` in this case. If the order is the only thing that matters then maybe I've got the answer and thank you. – maidamai Nov 24 '20 at 09:01
  • @DanielLangr Thanks for your patience. This is not a real-world scenario and I will not write code like this in my work. But If we can imagine that there is some work to be done and need both `mutex` here, would there be a deadlock risk? – maidamai Nov 24 '20 at 09:09
  • your example is still a bit too toy example. In your code the mutexes are always locked in same order, there is no chance to confuse the order. (Actually there is no reason to have two mutexes). Consider an example where you have in addition to `thread_func` a `thread_func2` and the two are written by different persons. One likes to sort their stuff in ascending order the other in descending order. – 463035818_is_not_an_ai Nov 24 '20 at 09:23
  • @idclev463035818 So the order is the only thing that matters? If I can always lock them in the same order I will never get a deadlock? – maidamai Nov 24 '20 at 09:25
  • "If I can always lock them in the same order I will never get a deadlock?" You are too optimistic. Programmers are humans and humans do make errors. A deadlock is something you want to avoid at all cost. If you can choose between the right tool and relying on nobody making a mistake, you should choose the tool – 463035818_is_not_an_ai Nov 24 '20 at 09:27
  • @idclev463035818 Yes I can not guarantee the order and I will always use `std::scoped_lock` in these cases. The only thing I want to make sure of is "Does the order the only thing that may cause the deadlock?" – maidamai Nov 24 '20 at 09:31
  • a deadlock is always related to the order in which you lock. Actually none of the answers in the dupe explain in a beginner friendly fashion what a deadlock is. I suggest you to go a step back and do some research on what a deadlock is and when it can occur. Once you got that, it should be easier to see the difference between lock_guard and scoped_lock – 463035818_is_not_an_ai Nov 24 '20 at 09:33
  • @idclev463035818 I read something about `memory order` and `instruction reorder` things recently. So CPU will always lock the mutex in the program order? – maidamai Nov 24 '20 at 09:38
  • 1
    https://stackoverflow.com/a/3130212/4117728 – 463035818_is_not_an_ai Nov 24 '20 at 09:40
  • 1
    @maidamai _Memory order_ and _instruction order_ is unrelated to your problem. Locking/unlocking mutextes must involve memory barriers. As suggested, try to find some material about deadlocks related to multiple mutexes. (You can have a deadlock caused by reordering of memory operations, but that is a different problem.) – Daniel Langr Nov 24 '20 at 10:16

0 Answers0