【发布时间】:2020-09-26 18:44:04
【问题描述】:
一般来说,声明一个交换并移动 noexcept 是一个很好的做法,因为这样可以提供一些异常保证。 同时编写一个线程安全的类通常意味着添加一个互斥锁来保护内部资源免受竞争。 如果我想为这样的类实现交换函数,直接的解决方案是以安全的方式锁定交换的两个参数的资源,然后执行资源交换,例如,在这个问题的答案中明确回答: Implementing swap for class with std::mutex.
这种算法的问题在于互斥锁不是noexcept,因此swap严格来说不能是noexcept。有没有一种解决方案可以用互斥锁安全地交换一个类的两个对象?
我想到的唯一可能性是将资源存储为句柄,以便交换成为可以原子完成的简单指针交换。 否则,人们可能会将锁定异常视为不可恢复的错误,无论如何都应该终止程序,但这种解决方案感觉就像是一种将灰尘放在地毯下的方法。
编辑: 正如在 cmets 中得出的那样,我知道互斥体抛出的异常不是任意,但是问题可以这样改写:
是否有可靠的做法来限制互斥锁可能引发的情况,而这实际上是一个不可恢复的操作系统问题? 我想到的是在交换算法中检查要交换的两个对象是否不相同。这是一个明显的死锁情况,在最好的情况下会触发异常,但可以轻松检查。 是否有其他类似的触发器可以安全地检查以使交换函数变得健壮并且几乎没有任何重要的情况?
【问题讨论】:
-
对我来说锁定移动似乎没有意义,因为一旦移动结束,线程可能会在重新分配之前开始使用移动的值。所以在这种情况下,无论如何你都会明确地锁定移动和赋值
-
@AlexLarionov 当然,移动共享对象是不安全的,但这是用户问题。我不确定移动时不锁定是否会使情况变得更好。无论如何,交换的问题仍然存在
-
作为最后的手段,您可以在锁定时捕获所有异常。尽管执行时间可能变得不可预测
-
是的,但这意味着要么跳过交换,要么在未锁定的资源上执行交换,这可能使其处于未定义状态。我想我宁愿终止而不是默默地制造这样一个潜在的混乱
-
@Triskeldeian 我认为
std::mutex大多数实现在锁定已锁定的互斥锁或使用未初始化的互斥锁时尝试返回错误。反正这些都是UB。我认为lock是noexcept(false)的意义在于发出程序错误的信号,而不是处理它们。想想~unique_lock:它是noexcept(true),但调用unlock是noexcept(false)。如果出现故障,整个程序就会终止——这就是大多数互斥锁的使用方式。