• 死锁检测


    曾经参与过的一款网络游戏,其服务器使用了异常复杂的多线程序解决方案。导致应用层程序员编写的代码很容易就出现死锁。

    最终,公司的一个老员工,只能开发了一个死锁检测框架,在debug模式下运行时,只要发生死锁就会打印出调用堆栈。

    虽然说这个框架基本可以在上线前把所有的死锁都检测了出来,但是,规根到底这是设计不合理造成的,多线程利用好了会提升

    应用的效率,用不好的话,除了影响效率外,对上层开发简直是灾难。

    下面说说那个检测方法,其实方法挺简单的。

    有两个容器,一个用于保存线程正在请求的锁,一个用于保存线程已经持有的锁。每次加锁之前都会做如下检测:

    1)检测当前正在请求的锁是否已经被其它线程持有,如果有,则把那些线程找出来

    2)遍历第一步中返回的线程,检查自己持有的锁是否正被其中任何一个线程请求

     如果第二步返回真,表示出现了死锁

    下面是简单的实现:

    appMutex.h

    #ifndef _APPMUTEX_H
    #define _APPMUTEX_H
    #include <iostream>
    #include "lock.h"
    #include <map>
    #include <list>
    class appMutex;
    static Lock gMtx;

    //记录了线程当前正在请求的锁
    class mtxReqMgr
    {
    public:
    static bool check(pthread_t pid);
    static void reqMutex(appMutex *mtx);
    static void clearReq(appMutex *mtx);
    private:
    static std::map<pthread_t,appMutex*> reqMap;//每个线程只可能请求一个锁
    };
    //记录了线程已经持有的锁
    class mtxHoldMgr
    {
    public:
    static bool check(pthread_t pid,appMutex *mtx);
    static bool check(appMutex *mtx,std::list<pthread_t> &ret);
    static void hold(appMutex *mtx);
    static void release(appMutex *mtx);
    //释放掉所有已经持有的锁
    static void releaseAll();

    private:
    static std::map<pthread_t,std::list<appMutex*> > holdMap;//每个线程可能拥有好几个锁
    };

    class appMutex : private Lock
    {
    friend class mtxHoldMgr;
    public:
    appMutex(const char *name):name(name)
    {}

    void lock()
    {
    /*这里执行死锁检测,检测规则
    1)检测当前正在请求的锁是否已经被其它线程持有,如果有,则把那些线程找出来
    2)遍历第一步中返回的线程,检查自己持有的锁是否正被其中任何一个线程请求
    如果第二步返回真,表示出现了死锁
    */
    std::list<pthread_t> mtxHolds;
    if(mtxHoldMgr::check(this,mtxHolds))
    {
    std::list<pthread_t>::iterator it = mtxHolds.begin();
    std::list<pthread_t>::iterator end = mtxHolds.end();
    for( ; it != end; ++it)
    {
    if(mtxReqMgr::check(*it))
    {
    mtxHoldMgr::releaseAll();
    printf("dead lock in require %s,thread:%u/n",name.c_str(),pthread_self());
    exit(0);
    }
    }
    }

    mtxReqMgr::reqMutex(this);
    Lock::lock();
    mtxReqMgr::clearReq(this);
    mtxHoldMgr::hold(this);
    }

    void unlock()
    {
    Lock::unlock();
    mtxHoldMgr::release(this);
    }
    private:
    void release()//只有在出现死锁时才会调用
    {
    Lock::unlock();
    }

    private:
    std::string name;
    };
    #endif

    appMutex.cpp

    #include "appMutex.h"

    std::map<pthread_t,appMutex*> mtxReqMgr::reqMap;
    std::map<pthread_t,std::list<appMutex*> > mtxHoldMgr::holdMap;
    //释放掉所有已经持有的锁
    void mtxHoldMgr::releaseAll()
    {
    Scope_lock _guard(gMtx);
    pthread_t pid = pthread_self();
    std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
    if(it != holdMap.end())
    {
    while(!it->second.empty())
    {
    appMutex *_appmtx = it->second.back();
    it->second.pop_back();
    _appmtx->release();
    }
    }
    }
    bool mtxReqMgr::check(pthread_t pid)
    {
    Scope_lock _guard(gMtx);
    pthread_t selfpid = pthread_self();
    std::map<pthread_t,appMutex*>::iterator it = reqMap.find(pid);
    if(it != reqMap.end() && it->second != NULL)
    {
    return mtxHoldMgr::check(selfpid,it->second);
    }
    return false;
    }
    void mtxReqMgr::reqMutex(appMutex *mtx)
    {
    Scope_lock _guard(gMtx);
    pthread_t pid = pthread_self();
    std::map<pthread_t,appMutex*>::iterator it = reqMap.find(pid);
    if(it == reqMap.end())
    {
    reqMap.insert(std::make_pair(pid,mtx));
    }
    else
    {
    it->second = mtx;
    }
    }
    void mtxReqMgr::clearReq(appMutex *mtx)
    {
    Scope_lock _guard(gMtx);
    pthread_t pid = pthread_self();
    std::map<pthread_t,appMutex*>::iterator it = reqMap.find(pid);
    if(it != reqMap.end())
    it->second = NULL;
    else
    {
    printf("it must be error %s %d /n",__FILE__,__LINE__);
    }
    }
    bool mtxHoldMgr::check(pthread_t pid,appMutex *mtx)
    {
    Scope_lock _guard(gMtx);
    std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
    if(it != holdMap.end())
    {
    std::list<appMutex*>::iterator lit = it->second.begin();
    std::list<appMutex*>::iterator lend = it->second.end();
    for( ; lit != lend; ++lit)
    {
    if(mtx == *lit)
    {
    return true;
    }
    }
    }
    return false;
    }
    bool mtxHoldMgr::check(appMutex *mtx,std::list<pthread_t> &ret)
    {
    Scope_lock _guard(gMtx);
    pthread_t pid = pthread_self();
    std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.begin();
    std::map<pthread_t,std::list<appMutex*> >::iterator end = holdMap.end();
    for( ; it != end; ++it)
    {
    if(it->first == pid)
    continue;
    std::list<appMutex*>::iterator lit = it->second.begin();
    std::list<appMutex*>::iterator lend = it->second.end();
    for( ; lit != lend; ++lit)
    {
    if(mtx == *lit)
    {
    ret.push_back(it->first);
    break;
    }
    }

    }
    return !ret.empty();
    }
    void mtxHoldMgr::hold(appMutex *mtx)
    {
    Scope_lock _guard(gMtx);
    pthread_t pid = pthread_self();
    std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
    if(it == holdMap.end())
    {
    std::list<appMutex*> tmp;
    tmp.push_back(mtx);
    holdMap.insert(std::make_pair(pid,tmp));
    }
    else
    {
    it->second.push_back(mtx);
    }
    }
    void mtxHoldMgr::release(appMutex *mtx)
    {
    Scope_lock _guard(gMtx);
    pthread_t pid = pthread_self();
    std::map<pthread_t,std::list<appMutex*> >::iterator it = holdMap.find(pid);
    if(it != holdMap.end())
    {
    if(mtx != it->second.back())
    {
    //释放锁的顺序跟加锁的顺序不一致
    printf("it must be error %s %d /n",__FILE__,__LINE__);
    }
    else
    it->second.pop_back();
    }
    else
    printf("it must be error %s %d /n",__FILE__,__LINE__);
    }

    在两个线程中分别如下调用,就会看到死锁警告了

    appMutex a("a");

    appMutex b("b");





    threada:



    while(1)

    {



    a.lock();

    Thread::sleep(1);

    b.lock();



    b.unlock();

    a.unlock();

    }





    threadb:





    while(1)

    {

    b.lock();

    a.lock();



    a.unlock();

    b.unlock();



    }




  • 相关阅读:
    知多少进程?
    提高.NET应用性能
    战术设计DDD
    win7下exe文件设置为开机启动
    CQRS项目
    DDD总览
    ML.Net Model Builder
    MySQL主主复制搭建教程收集(待实践)
    MySQL主从复制搭建教程收集(待实践)
    MySQL集群方案收集
  • 原文地址:https://www.cnblogs.com/sniperHW/p/2429638.html
Copyright © 2020-2023  润新知