c++死锁模拟与检测

 死锁在并发编程里面是很难预防的,锁和线程比较少的情况下还能通过分析代码,理出线索进行避免,一旦超过一定数量感觉就会无从下手,必须通过自动化工具进行检测。以下示例对哲学家就餐问题进行模拟,动态构建线程依赖拓扑图,并用Floyd - Warshall找环算法,找寻死锁链,在程序运行过程中如果死锁链成型,将会清晰地给出锁链信息,对下一步的死锁解除提供有力指导依据。

#include <thread>
#include <chrono>
#include <mutex>
#include <condition_variable>
#include <string>
#include <set>
#include <map>
#include <iostream>

#define STRINGIFY(s) #s
#define TOSTRING(s) STRINGIFY(s)
#define _CONCATE_(x,y) __FILE__##x##y
#define CONCATE(x,y) _CONCATE_(x,y)
#define LOCKNAME CONCATE(":",TOSTRING(__LINE__))

#define LOCK_INIT() {LOCKNAME, new std::mutex}

typedef struct _lock_t
{
    std::mutex* mutex;
    const char* name;
    std::thread::id tid;
    _lock_t() :_lock_t(nullptr, nullptr) {}
    _lock_t(const char* _name, std::mutex* mtx) :name(_name), mutex(mtx) {}
    _lock_t(_lock_t&& lk)
    {
        this->mutex = lk.mutex;
        this->name = lk.name;
        lk.mutex = nullptr;
    }
    _lock_t(const _lock_t& lk) = delete;
    _lock_t(const _lock_t&& lk) = delete;
    _lock_t& operator=(const _lock_t&) = delete;

    ~_lock_t()
    {
        if (mutex != nullptr)
        {
            delete mutex;
        }
    }
}lock_t;

using edge = std::pair<std::thread::id, std::thread::id>;
static auto* vertices = new std::set<std::thread::id>();
static auto* edges = new std::map<edge,std::string>();

static std::multimap<std::thread::id,std::string> held_locks;
static std::mutex GL;

static void check_cycles()//Floyd - Warshall 算法判断是否有环
{
    for (auto v : *vertices)
        for (auto u : *vertices)
            for (auto w : *vertices)
                {
                    auto uv = edges->find({ u,v });
                    auto vw = edges->find({ v,w });
                    if (uv!= edges->end() &&  vw!= edges->end())
                        edges->insert({{ u,w },uv->second+"->"+vw->second});
                }
    std::cout << "Lockdep check: " << std::endl;
    for (auto e : *edges)
    {
        std::cout << "    " << e.first.first<< "->" << e.first.second << std::endl;
        if (e.first.first == e.first.second)
             std::cout << "\033[31m!!!Warning: Cycle detected, Path: " << e.second <<"\033[0m" << std::endl;
    }
}

void lock(lock_t* lk)
{
    if (lk == nullptr|| lk->mutex == nullptr || lk->name == nullptr)
        return;
     std::thread::id tid = std::this_thread::get_id();
    {
        std::lock_guard<std::mutex> locker(GL);
        bool update = false;
       
        vertices->insert(tid);
        //unsigned int threadId = ((_Thrd_t*) & lk->tid)->_Id;
        std::thread::id lkTid = lk->tid;
        auto it = held_locks.find(lkTid);
        if(it != held_locks.end())//如果lk已被某个线程占有,则表明当前线程依赖了该线程
        {
            edges->insert({{tid,it->first},lk->name});//增加一条依赖边
            update = true;
        }
        if (update)
            check_cycles();
        held_locks.insert({ tid,lk->name });
    }
    lk->mutex->lock();
    lk->tid = tid;
}

void unlock(lock_t* lk)
{
    if (lk == nullptr || lk->mutex == nullptr || lk->name == nullptr)
        return;
    lk->mutex->unlock();
    {
        std::lock_guard<std::mutex> locker(GL);
        std::pair<std::multimap<std::thread::id, std::string>::iterator, std::multimap<std::thread::id, std::string>::iterator> it =
            held_locks.equal_range(std::this_thread::get_id());
        while (it.first != it.second) 
        {
            if (it.first->second == std::string(lk->name))
                it.first = held_locks.erase(it.first);
            else
                it.first++;
        }
    }
}

lock_t lk1 = LOCK_INIT();
lock_t lk2 = LOCK_INIT();
lock_t lk3 = LOCK_INIT();

struct some_class
{
    lock_t lock;
    int data;
};

void object_init(struct some_class* obj, lock_t* lk)
{
    new(&obj->lock)lock_t(std::move(*lk));
    obj->data = 100;
}

void create_object()
{
    struct some_class* obj = new some_class;
    //lock_t lk = LOCK_INIT();
    //object_init(obj, &lk);
    lock(&obj->lock);
    unlock(&obj->lock);
    delete obj;
}

std::mutex startMtx;
std::condition_variable startCond;

void T_1()
{
    {
        std::unique_lock<std::mutex> locker(startMtx);
        startCond.wait(locker);
    }
    lock(&lk1);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    lock(&lk2);
    create_object();
    unlock(&lk1);
    unlock(&lk2);
}

void T_2()
{
    {
        std::unique_lock<std::mutex> locker(startMtx);
        startCond.wait(locker);
    }
    //std::this_thread::sleep_for(std::chrono::seconds(3));
    lock(&lk2);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    lock(&lk3);
    create_object();
    unlock(&lk2);
    unlock(&lk3);
}

void T_3()
{
    {
        std::unique_lock<std::mutex> locker(startMtx);
        startCond.wait(locker);
    }
    //std::this_thread::sleep_for(std::chrono::seconds(6));
    lock(&lk3);
    std::this_thread::sleep_for(std::chrono::milliseconds(10));
    lock(&lk1);
    create_object();
    unlock(&lk3);
    unlock(&lk1);
}


int main(int argc, char* argv[])
{
    std::thread t1 = std::thread(&T_1);
    std::thread t2 = std::thread(&T_2);
    std::thread t3 = std::thread(&T_3);
    std::cout<<"输入任意键开始测试..."<<std::endl;
    getchar();
    startCond.notify_all();//让3个线程同时开始竞争加锁,程序可能因为死锁问题卡死无法退出
    //注意,如果线程1实在太快了,连续拿lk1和lk2都成功了,就不会死锁,如果在T_1
    //拿到lk1,T_2拿到lk2,T_3拿lk3后都稍稍加点延时(10ms),这样死锁就很容易产生了。
    //死锁问题在真实生产环境中也是这样,不是必现问题,很难排查。
    t1.join();
    t2.join();
    t3.join();
    system("pause");
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值