C++ 并发编程:避免死锁

一、 死锁定义

1. 举个例子

试想有一个玩具, 这个玩具由两部分组成, 必须拿到这两个部分, 才能够玩。 例如, 一个玩
具鼓, 需要一个鼓锤和一个鼓才能玩。 现在有两个小孩, 他们都很喜欢玩这个玩具。 当其中
一个孩子拿到了鼓和鼓锤时, 那就可以尽情的玩耍了。 当另一孩子想要玩, 他就得等待另一
孩子玩完才行。 再试想, 鼓和鼓锤被放在不同的玩具箱里, 并且两个孩子在同一时间里都想
要去敲鼓。 之后, 他们就去玩具箱里面找这个鼓。 其中一个找到了鼓, 并且另外一个找到了
鼓锤。 现在问题就来了, 除非其中一个孩子决定让另一个先玩, 他可以把自己的那部分给另
外一个孩子; 但当他们都紧握着自己所有的部分而不给予, 那么这个鼓谁都没法玩。

2. 定义

现在没有孩子去争抢玩具, 但线程有对锁的竞争: 一对线程需要对他们所有的互斥量做一些
操作, 其中每个线程都有一个互斥量, 且等待另一个解锁。 这样没有线程能工作, 因为他们
都在等待对方释放互斥量。 这种情况就是死锁, 它的最大问题就是由两个或两个以上的互斥
量来锁定一个操作。

二、避免死锁建议

1. 避免嵌套锁

第一个建议往往是最简单的: 一个线程已获得一个锁时, 再别去获取第二个。 如果能坚持这
个建议, 因为每个线程只持有一个锁, 锁上就不会产生死锁。 即使互斥锁造成死锁的最常见
原因, 也可能会在其他方面受到死锁的困扰(比如: 线程间的互相等待)。 当你需要获取多个
锁, 使用一个 std::lock 来做这件事(对获取锁的操作上锁), 避免产生死锁。
std::lock例子

#include <mutex>
class some_big_object
{};
void swap(some_big_object& lhs,some_big_object& rhs)
{}

class X
{
private:
    some_big_object some_detail;
    mutable 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);
        std::lock_guard<std::mutex> lock_a(lhs.m,std::adopt_lock);
        std::lock_guard<std::mutex> lock_b(rhs.m,std::adopt_lock);
        swap(lhs.some_detail,rhs.some_detail);
    }
};

int main()
{}

2. 避免在持有锁时调用用户提供的代码

第二个建议是次简单的: 因为代码是用户提供的, 你没有办法确定用户要做什么; 用户程序
可能做任何事情, 包括获取锁。 你在持有锁的情况下, 调用用户提供的代码; 如果用户代码
要获取一个锁, 就会违反第一个指导意见, 并造成死锁(有时, 这是无法避免的)。 当你正在写
一份通用代码, 每一个操作的参数类型, 都在用户提供的代码中定义, 就
需要其他指导意见来帮助你

3. 使用固定顺序获取锁

当硬性条件要求你获取两个以上(包括两个)的锁, 并且不能使用 std::lock 单独操作来获取它
们;那么最好在每个线程上, 用固定的顺序获取它们获取它们(锁).

4. 使用锁的层次结构

虽然, 这对于定义锁的顺序, 的确是一个特殊的情况, 但锁的层次的意义在于提供对运行时
约定是否被坚持的检查。 这个建议需要对你的应用进行分层, 并且识别在给定层上所有可上
锁的互斥量。 当代码试图对一个互斥量上锁, 在该层锁已被低层持有时, 上锁是不允许的。
你可以在运行时对其进行检查, 通过分配层数到每个互斥量上, 以及记录被每个线程上锁的
互斥量。 下面的代码列表中将展示两个线程如何使用分层互斥。
层次锁示例代码

#include <mutex>
#include <stdexcept>

class hierarchical_mutex
{
    std::mutex internal_mutex;
    unsigned long const hierarchy_value;
    unsigned long previous_hierarchy_value;
    static thread_local unsigned long this_thread_hierarchy_value;

    void check_for_hierarchy_violation()
    {
        if(this_thread_hierarchy_value <= hierarchy_value)
        {
            throw std::logic_error("mutex hierarchy violated");
        }
    }
    void update_hierarchy_value()
    {
        previous_hierarchy_value=this_thread_hierarchy_value;
        this_thread_hierarchy_value=hierarchy_value;
    }
public:
    explicit hierarchical_mutex(unsigned long value):
        hierarchy_value(value),
        previous_hierarchy_value(0)
    {}
    void lock()
    {
        check_for_hierarchy_violation();
        internal_mutex.lock();
        update_hierarchy_value();
    }
    void unlock()
    {
        this_thread_hierarchy_value=previous_hierarchy_value;
        internal_mutex.unlock();
    }
    bool try_lock()
    {
        check_for_hierarchy_violation();
        if(!internal_mutex.try_lock())
            return false;
        update_hierarchy_value();
        return true;
    }
};
thread_local unsigned long
    hierarchical_mutex::this_thread_hierarchy_value(ULONG_MAX);       

int main()
{
    hierarchical_mutex m1(42);
    hierarchical_mutex m2(2000);
}

5. 考虑使用标准库std::unique_lock——灵活的锁

std::unique_lock例子

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值