Ежедневный бит (е) C ++ # 13, рекурсивный вариант мьютекса: std:: recursive_mutex

std::recursive_mutex — это вариант std::mutex, который позволяет одному потоку удерживать несколько блокировок. Только после снятия всех блокировок другие потоки могут захватить этот мьютекс.

Эта функция удобна при разработке потокобезопасных структур данных.

struct NonRecursive {
    void push_back(int value) {
        std::unique_lock lock(mux_);
        // We already hold mux_, so we couldn't call reserve()
        if (size_ == capacity_)
            reserve_impl(capacity_ == 0 ? 64 : capacity_ * 2);
        data_[size_++] = value;
    }
    void reserve(size_t cnt) {
        std::unique_lock lock(mux_);
        reserve_impl(cnt);
    }
private:
    // reserve_impl expects mux_ to be held by the caller
    void reserve_impl(size_t cnt) {
        auto new_data = std::make_unique<int[]>(cnt);
        std::ranges::copy(std::span(data_.get(), std::min(size_, cnt)),
                          new_data.get());
        data_ = std::move(new_data);
        capacity_ = cnt;
        size_ = std::min(size_, capacity_);
    }
    std::mutex mux_;
    std::unique_ptr<int[]> data_;
    size_t size_ = 0;
    size_t capacity_ = 0;
};

struct Recursive {
    void push_back(int value) {
        std::unique_lock lock(mux_);
        // holding a recursive mutex multiple times is fine
        if (size_ == capacity_)
            reserve(capacity_ == 0 ? 64 : capacity_ * 2);
        data_[size_++] = value;
    }
    void reserve(size_t cnt) {
        std::unique_lock lock(mux_);
        auto new_data = std::make_unique<int[]>(cnt);
        std::ranges::copy(std::span(data_.get(), std::min(size_, cnt)),
                          new_data.get());
        data_ = std::move(new_data);
        capacity_ = cnt;
        size_ = std::min(size_, capacity_);
    }
private:
    std::recursive_mutex mux_;
    std::unique_ptr<int[]> data_;
    size_t size_ = 0;
    size_t capacity_ = 0;
};

Откройте этот пример в Compiler Explorer.