4
 ~scoped_lock()
  { std::apply([](auto&... __m) { (__m.unlock(), ...); }, _M_devices); }

どうやって理解する[](auto&... __m) { (__m.unlock(), ...);?インラムダを理解していませ...ん。また、これがミューテックスを逆の順序で解放する方法もわかりません。

@HolyBlackCat が言うように、
(__m.unlock(), ...)を意味(__m1.unlock(),(__m2.unlock(), (__m3.unlock(), (...))))しますが、逆の順序でのロック解除は実装しません。

cppreference.com で:

scoped_lock オブジェクトが作成されたスコープから制御が離れると、scoped_lock は破棄され、ミューテックスは逆の順序で解放されます。

これを確認するために、次のような実験を行います。

#include <chrono>
#include <iostream>
#include <mutex>
#include <thread>

class mymutex : public std::mutex {
 public:
   void lock() {
     std::mutex::lock();
     std::cout << "mutex " << _i << " locked" << std::endl;
   }
  mymutex(int i): _i(i){}
   bool try_lock() {
    bool res = std::mutex::try_lock();
    if (res) {
      std::cout << "mutex " << _i << " try locked" << std::endl;
    }
    return res;
   }
  void unlock() {
    std::mutex::unlock();
    std::cout << "mutex " << _i << " unlocked" << std::endl;
  }
 private:
  int _i;
};

class Speaking {
 private:
  int a;
  mymutex my1;
  mymutex my2;
  mymutex my3;

public:
  Speaking() : a(0), my1(1), my2(2), my3(3){};
  ~Speaking() = default;
  void speak_without_lock();
  void speak_with_three_lock();
};

void Speaking::speak_without_lock() {
  std::cout << std::this_thread::get_id() << ": " << a << std::endl;
  a++;
}

void Speaking::speak_with_three_lock() 
{
  std::scoped_lock<mymutex, mymutex, mymutex> scoped(my1, my2, my3);
  speak_without_lock();
}

int main() {
  Speaking s;

  s.speak_with_three_lock();

  return 0;
}
mutex 1 locked
mutex 2 try locked
mutex 3 try locked
1: 0
mutex 1 unlocked
mutex 2 unlocked
mutex 3 unlocked

では、cppreference は間違いを犯しますか?

4

1 に答える 1

4

この詳細については、cppreference.com が間違っていると思います。C++17 は次のように述べています。

~scoped_lock();

効果: [0, sizeof...(MutexTypes)) 内のすべての i に対して、get(pm).unlock()

これは、ロックが取得されたのと同じ順序でロックが解放されることを意味します。

デッドロックを防ぐために、ロックを取得した逆の順序でロックを解放する必要はないことに注意してください。必要なのは、常に同じ順序でロックを取得することだけです。

于 2021-09-07T06:45:23.753 に答える