読者です 読者をやめる 読者になる 読者になる

std::unique_lockはムーブ可能

C++

ミューテックスクラスのオブジェクトを、スコープ内で確実にロック/アンロックするためのクラスとして、std::lock_guardとstd::unique_lockという2つのクラスが提供されています。


std::lock_guardは単純な用途のためのクラスなのでムーブできませんが、std::unique_lockはムーブできます。
たとえば、ロック済みミューテックスを返す関数をstd::unique_lockで作ると、受け取った側が使い終わったら自動的にアンロックしてくれます。

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

class X {
    int value_ = 0;
    mutable std::mutex mtx_;
public:
    void add_value()
    {
        std::unique_lock<std::mutex> lk = get_locked_mutex();

        ++value_;
    } // ここでアンロックされる

    int get() const
    {
        std::unique_lock<std::mutex> lk = get_locked_mutex();
        return value_;
    }

private:
    std::unique_lock<std::mutex> get_locked_mutex() const
    {
        return std::unique_lock<std::mutex>(mtx_); // ロックして返す
    }
};

int main()
{
    X x;

    std::thread t1([&] { x.add_value(); });
    std::thread t2([&] { x.add_value(); });

    t1.join();
    t2.join();

    std::cout << x.get() << std::endl;
}
2

関数の引数としてロック済みミューテックスを渡すと、渡された側のスコープ終了時に自動的にアンロックされます。

#include <iostream>
#include <mutex>
#include <thread>
#include <atomic>

class spinlock {
private:
  typedef enum {Locked, Unlocked} LockState;
  std::atomic<LockState> state_;

public:
  spinlock() : state_(Unlocked) {}
  
  void lock()
  {
    // 現在の状態をLockedと入れ替える
    while (state_.exchange(Locked, std::memory_order_acquire) == Locked) {
      // busy-wait...アンロックされるまで待機
    }
    std::cout << "lock" << std::endl;
  }

  void unlock()
  {
    // 値をUnlockedに更新
    state_.store(Unlocked, std::memory_order_release);
    std::cout << "unlock" << std::endl;
  }
};

class X {
    int value_ = 0;
    mutable spinlock mtx_;
public:
    void add_value()
    {
        add_value_impl(get_locked_mutex()); // ロック済みミューテックスをムーブ
    }

    int get() const
    {
        std::unique_lock<spinlock> lk = get_locked_mutex();
        return value_;
    }

private:
    void add_value_impl(std::unique_lock<spinlock>)
    {
        ++value_;
        std::cout << "add valued" << std::endl;
    } // ここでアンロックされる

    std::unique_lock<spinlock> get_locked_mutex() const
    {
        return std::unique_lock<spinlock>(mtx_); // ロックして返す
    }
};

int main()
{
    X x;

    std::thread t1([&] { x.add_value(); });
    std::thread t2([&] { x.add_value(); });

    t1.join();
    t2.join();

    std::cout << x.get() << std::endl;
}
lock
add valued
unlock
lock
add valued
unlock
lock
unlock
2


参照:
std::unique_lock - cpprefjp
std::lock_guard - cpprefjp
Thread-Safe Copy and Move Constructors