类似问题:
用 std::vector 调用 std::lock ()
使用 std::lock_guard 数组锁定 std::mutex 数组
总结: 第二个显示锁定静态大小的数组,第一个指的是 boost 库 boost::lock() ,它接受两个迭代器。然而,boost::lock 不支持 RAII,而 std::lock 则不支持。我正在寻找如下所示的解决方案
std::lock(m1, m2);
std::lock_guard l1{m1, std::adopt_lock};
std::lock_guard l2{m2, std::adopt_lock};
或者是否需要所有权转让
std::unique_lock<std::mutex> l1{m1, std::defer_lock};
std::unique_lock<std::mutex> l2{m2, std::defer_lock};
std::lock(l1, l2);
或使用 std::scoped_lock
std::scoped_lock<std::mutex> lk{ m1, m2 };
但是,需要锁定的互斥锁属于运行时已知的多个对象。因此,我不能使用 std::lock 或 std::scoped_lock。
std::vector<std::mutex> mutexes{};
mutexes.push_back(std::move(m1));
mutexes.push_back(std::move(m2));
std::scoped_lock<std::mutex> lk{ mutexes }; // ERROR
项目:
我有一个以 DAG 结构为中心的项目。 DAG 包含多对一和一对多关系,并且(理应如此)支持祖先方向和后代方向的遍历。为了防止死锁,我尝试尽可能仅在一个方向(后代)实现所有算法。然而,在某些时候,代码不可避免地包含祖先方向。因此,我尝试在遍历结构时一次性锁定相关的互斥锁。例如,在下降方向:
DAGNode DN{};
std::vector<DAGNode> descendants{ DN.get_descendants() };
// Assume we have two descendants
auto& des1{ descendants[0] };
auto& des2{ descendants[1] };
std::scoped_lock<std::mutex> lk{DN.m, des1.m, des2.m};
祖先方向的代码类似。
由于DAG同时支持多对一和一对多关系,因此DAG强烈要求必须像上面一样一起执行锁定。对我来说,由于多对一和一对多关系,定义总顺序并按该顺序锁定看起来并不合理。例如,考虑节点 A1、A2、B1、B2、B3,其中 A1 是 B1 和 B2 的祖先; A2 是 B2 和 B3 的祖先。所以当A1参与算法时,(A1,B1,B2)必须被锁定,而A2必须分别锁定(A2,B2,B3)。因此,B2必须被锁定在分别从A1和A2开始的两次遍历中。这是多对一关系的结果,但一对多也会导致类似的情况。实际上,从这个简单的例子可以看出,同方向的遍历也可能会导致死锁(由于B2)。因此,唯一的解决方案是立即锁定所有互斥体。
总而言之,我的问题是:
不是一站式 API,但您可以:
#include <boost/thread.hpp>
#include <mutex>
struct bulk_guard {
std::list<std::unique_lock<std::mutex>> guards_;
template <typename Lockables> explicit bulk_guard(Lockables& lockables) {
boost::lock(lockables.begin(), lockables.end());
for (auto& lockable : lockables)
guards_.emplace_back(lockable, std::adopt_lock);
}
};
#include <array>
int main() {
std::array<std::mutex, 10> mxs;
bulk_guard bulk(mxs);
}
这是一个难题,我通常可以通过锁定更粗粒度的组(例如 DDD 中的聚合,但我在各种软件中识别这些实体;通常它是一个分配单位(例如“任务”)或所有权单位(例如文件系统、目录、进程/线程等)
我相信这已经是
boost::lock
合理的。在大多数实现(包括 POSIX)上,可锁定实现
不可移动,这意味着 C++ 对象标识(通俗地说“地址”)可以充当总顺序。 我也活过那本书。我认为它是最好的资源之一,尽管它似乎主要集中在基于任务的并发性上,而且您似乎更多地从基于参与者的并发范例中获得它。我对此并不热衷(我认为它往往与性能相反,这让我想起了许多失败的面向参与者的并发性的承诺,例如 Corba 和 COM+ - 配有 MTS
我知道你可能可以在 Eiffel/Smalltalk 文献中找到更多关于这个观点的信息,但它不再那么流行可能是有原因的。