从“C ++ Concurrency in Action”一书中的例子
示例(3.2.4)
friend void swap(X& lhs, X& rhs)
{
if (&lhs == &rhs)
return;
std::lock(lhs.m, rhs.m); #1
std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock); #2
std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock); #3
swap(lhs.some_detail, rhs.some_detail); 
}
当我们在#2和#3上时,如果在另一个线程上抛出异常怎么办?
另一个例子(3.2.6)看起来更好(虽然unique_guard
更慢更大,但更慢更大)
friend void swap(X& lhs, X& rhs)
{
if (&lhs == &rhs)
return;
std::unique_guard<std::mutex> lock_a(lhs.m, std::defer_lock); #1
std::unique_guard<std::mutex> lock_b(rhs.m, std::defer_lock); #2
std::lock(lhs.m, rhs.m); #3
swap(lhs.some_detail, rhs.some_detail); 
}
3.2.4的例子不是例外安全吗?或者我想念什么?感谢。
天真的例子
class some_big_object;
void swap(some_big_object& lhs,some_big_object& rhs);
class X
{
private:
some_big_object some_detail;
std::mutex m;
public:
X(some_big_object const& sd):some_detail(sd){}
friend void swap(X& lhs, X& rhs)
{
if(&lhs==&rhs)
return;
std::lock(lhs.m,rhs.m); #A1
std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock); #A2
std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock); #A3
swap(lhs.some_detail,rhs.some_detail);
}
};
void threadA()
{
X A, B;
//do something
swap(A, B);
}
void threadB()
{
//do something
throw std::runtime_error("error");
}
void testSwap()
{
std::thread tA(threadA);
std::thread tB(threadB);
tA.join();
tB.join();
}
我的问题是,如果threadA处理#A2时threadB抛出异常怎么办? 互斥锁已经锁定,但lock_guard可能尚未为互斥锁做好准备。
答案 0 :(得分:4)
他们看起来对我来说是安全的。
在第一个例子中,可以抛出相等比较,或者在#1处对std::lock
的调用可以抛出,但是如果它执行则函数退出而不改变任何东西。标准表示,#2和#3的对象初始化不能抛出。对swap
的调用可能会抛出,但如果确实如此,则unique_lock
析构函数会解锁互斥锁。
在第二个示例中,相等比较可以抛出,#1和#2的初始化为noexcept
,std::lock
调用可以抛出,但如果确实,函数退出时没有效果。对swap的调用可能会抛出,但如果确实如此,则unique_lock
析构函数会解锁互斥锁。
您似乎暗示单独线程中的异常与执行交换的线程交互,但事实并非如此。一个线程中的异常不会影响其他线程的执行。
答案 1 :(得分:-1)
我将不得不拿到这本书,看看它实际上是什么, 因为 受到高度重视。从来没有:
实现线程安全交换非常重要,无论如何 情况。通常,必须在a处理线程安全问题 更高层次。
您展示的解决方案都不起作用;它们都会导致死锁。
唯一有效的解决方案是为 all 设置一个互斥锁
X
的实例,并在其上方使用unique_guard
swap
功能。
(最后,异常不会在这里进入。但你考虑它们是正确的,因为它们可以使安全的解决方案不安全。)