【发布时间】:2015-05-24 00:09:13
【问题描述】:
我想将可变数量的互斥锁存储在一个容器中,比如向量或双端队列。
在其中一个用例中,我需要可靠且无死锁地锁定所有互斥锁。我还想有异常安全保证,如果抛出异常,所有的互斥锁就好像没有发生锁定一样。
我正在尝试做类似的事情:
std::vector<std::mutex> locks_(n);
std::vector<std::lock_guard<std::mutex> > guards(n);
for(int i = 0; i < n; i++) {
std::lock_guard<std::mutex> guard(locks_[i]);
guards.emplace_back(std::move(guard));
}
但它没有编译,给我:
/usr/include/c++/4.8/ext/new_allocator.h:120:4:错误:使用已删除 函数‘std::lock_guard<_mutex>::lock_guard(const std::lock_guard<_mutex>&) [with _Mutex = std::mutex]'
我想lock_guards被销毁时也可能会出现问题,因为与构造相比,顺序必须颠倒,但标准为我们节省了:
删除表达式将调用析构函数(如果有) 被删除的对象或数组的元素。在一个 数组,元素将按地址递减顺序销毁 (也就是说,按照它们的构造函数完成的相反顺序;参见 12.6.2)。
这种方法有什么潜在的缺陷吗?如何让它发挥作用?
编辑
其实我错了,看来vector并不能保证特定的破坏顺序。看到这个问题:Order of destruction of elements of an std::vector
编辑2
Q:如果用例是:
所有互斥锁都由不同的线程以任意顺序锁定/解锁(但是每个线程一次只使用 1 个互斥锁), 但有时我需要在另一个线程中以安全的方式锁定所有互斥锁。
【问题讨论】:
-
lock_guard不能移动(也不能复制)。unique_lockOTOH 可以移动。 -
编译错误不是抱怨 lock_guard 不是可复制构造的吗?这与删除顺序无关。 lock_guard 删除了它的复制构造函数并且没有定义移动构造函数,所以你不能将它移动到向量中。见en.cppreference.com/w/cpp/thread/lock_guard
-
@Appleshell 我不太明白为什么要调用复制构造函数,我明确告诉移动警卫而不是复制
-
@Appleshell 关于删除顺序的声明是说可能存在死锁。我说“我猜可能还有问题……”
-
@iggy 定义(或删除)复制构造函数时,不会生成移动构造函数。 lock_guard 删除了它的拷贝构造函数,因此既没有移动也没有拷贝构造函数。