c++ 有什么方法可以锁定互斥体列表吗?

s5a0g9ez  于 2023-05-30  发布在  其他
关注(0)|答案(3)|浏览(208)

我的代码如下。一个Element包含一个指向mutex的指针,并且有一个向量包含所有的Element s。
如果我知道编译时元素的计数,我可以构造一个scoped_lock来锁定所有这些互斥锁。但是,我现在不知道向量的大小。那么,我怎样才能锁定所有的互斥锁并安全地生成锁保护呢?

#include <vector>
#include <mutex>

struct Element {
    Element() {
        mut = new std::mutex();
    }
    Element(Element && other) : mut(other.mut) {
        other.mut = nullptr;
    }
    ~Element() { delete mut; }
    std::mutex * mut;
};

int main() {
    std::vector<Element> v;
    v.push_back(Element());
    v.push_back(Element());
    v.push_back(Element());

    std::scoped_lock(*v[0].mut, *v[1].mut, *v[2].mut);
}
omtl5h9j

omtl5h9j1#

你可以试试这个被诅咒的代码:

struct MultiScopedLock {
  template <std::input_iterator I, std::sentinel_for<I> S>
  requires std::is_same_v<decltype(*std::declval<I>()), std::mutex &>
  MultiScopedLock(I begin, S end) {
    if constexpr (
        requires {requires std::random_access_iterator<I>; }
        && std::is_same_v<std::remove_cv<I>, std::remove_cv<S>>
    ){
        guards.reserve(end - begin);
    }
    try {
      for (; begin != end; ++begin) {
        guards.emplace_back(*begin);
      }
    } catch (...) {
      clear_guards();
      throw;
    }
  }

  // Need to delete all of this to guarantee unlocking order.
  MultiScopedLock(const MultiScopedLock &) = delete;
  MultiScopedLock(MultiScopedLock &&) = delete;
  MultiScopedLock &operator=(const MultiScopedLock &) = delete;
  MultiScopedLock &operator=(MultiScopedLock &&) = delete;

  ~MultiScopedLock() { clear_guards(); }

private:
  std::vector<std::unique_lock<std::mutex>> guards;

  void clear_guards() {
    // Release locks in reverse order.
    while (!guards.empty()) {
      guards.pop_back();
    }
  }
};

P.S.感谢@davis-herring的std::unique_lock建议。

inn6fuwd

inn6fuwd2#

我真的没有看到任何用例,我们需要存储一堆互斥锁并在一行中锁定/解锁它们,但无论如何,您可以编写自己的“lock_guard”,通过迭代std::vector来锁定/解锁它们。

**注意:**如果无法保持锁的顺序,则此解决方案会出现死锁问题。

示例

考虑下面的Element类(我只是以更简单的方式重写了它):

struct Element
{
    std::unique_ptr<std::mutex> mut;

    Element() = default;
    Element(Element && other) : mut{std::move(other.mut)}
    {}
};

你可以把你自己的“锁卫士”写为:

struct elements_lock
{
    std::vector<Element> & v_ref;

    elements_lock(std::vector<Element> & v) : v_ref{v}
    {
        for(Element & e : v)
            e.mut->lock();
    }
    ~elements_lock()
    {
        for(Element & e : v_ref)
            e.mut->unlock();
    }
};

并按以下方式使用它:

int main()
{
    std::vector<Element> v(3); // Create a vector of 3 Elements
    {
        elements_lock el_lock(v);  // Acquire the locks sequentially

        // [...]

    } // At el_lock destruction, the locks will be released sequentially

    return 0;
}
1qczuiv0

1qczuiv03#

您可以按照自己选择的顺序锁定互斥锁,并按照相反的顺序解锁互斥锁。请注意,这并不能保证当std::scoped_lock与多个互斥锁一起使用时,锁定的顺序适合避免死锁。

class [[nodiscard]] ElementsLock
{
    std::vector<std::mutex*> m_orderedMutexes;
public:
    // defining these results in deleted copy semantics
    ElementsLock(ElementsLock&&) noexcept = default;
    ElementsLock& operator=(ElementsLock&&) noexcept = default;

    ElementsLock(std::vector<Element> const& v)
    {
        m_orderedMutexes.reserve(v.size());
        for (auto& e : v)
        {
            m_orderedMutexes.push_back(e.mut);
        }
        std::sort(m_orderedMutexes.begin(), m_orderedMutexes.end(),
            [](void const* a, void const* b) { return reinterpret_cast<uintptr_t>(a) < reinterpret_cast<uintptr_t>(b); });

        auto lockPos = m_orderedMutexes.begin();
        try
        {
            for (auto const end = m_orderedMutexes.end(); lockPos != end; ++lockPos)
            {
                (**lockPos).lock();
            }
        }
        catch (...)
        {
            // unlock locked mutexes
            for (auto const begin = m_orderedMutexes.begin(); lockPos != begin;)
            {
                --lockPos;
                try
                {
                    (**lockPos).unlock();
                }
                catch (...)
                {
                    // cannot properly unlock the locked mutexes
                    std::terminate();
                }
            }

            throw;
        }
    }

    ~ElementsLock() noexcept
    {
        for (auto begin = m_orderedMutexes.begin(), pos = m_orderedMutexes.end(); pos != begin;)
        {
            --pos;
            (**pos).unlock();
        }
    }
};

int main() {
    std::vector<Element> v;
    v.push_back(Element());
    v.push_back(Element());
    v.push_back(Element());

    ElementsLock lock(v);
}

相关问题