这是swap(多线程)的异常安全实现吗?

时间:2013-08-04 20:44:53

标签: c++ multithreading c++11

从“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可能尚未为互斥锁做好准备。

2 个答案:

答案 0 :(得分:4)

他们看起来对我来说是安全的。

在第一个例子中,可以抛出相等比较,或者在#1处对std::lock的调用可以抛出,但是如果它执行则函数退出而不改变任何东西。标准表示,#2和#3的对象初始化不能抛出。对swap的调用可能会抛出,但如果确实如此,则unique_lock析构函数会解锁互斥锁。

在第二个示例中,相等比较可以抛出,#1和#2的初始化为noexceptstd::lock调用可以抛出,但如果确实,函数退出时没有效果。对swap的调用可能会抛出,但如果确实如此,则unique_lock析构函数会解锁互斥锁。

您似乎暗示单独线程中的异常与执行交换的线程交互,但事实并非如此。一个线程中的异常不会影响其他线程的执行。

答案 1 :(得分:-1)

我将不得不拿到这本书,看看它实际上是什么, 因为 受到高度重视。从来没有:

  • 实现线程安全交换非常重要,无论如何 情况。通常,必须在a处理线程安全问题 更高层次。

  • 您展示的解决方案都不起作用;它们都会导致死锁。

  • 唯一有效的解决方案是为 all 设置一个互斥锁 X的实例,并在其上方使用unique_guard swap功能。

(最后,异常不会在这里进入。但你考虑它们是正确的,因为它们可以使安全的解决方案不安全。)