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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
银行家算法是一种经典的避免死锁的算法,它的核心是预测系统的资源分配情况,避免在某些情况下导致资源不足而陷入死锁状态。以下是一个简单的银行家算法的实现: ```c++ #include <iostream> #include <vector> using namespace std; const int MAXN = 100; // 最大进程数 const int MAXM = 100; // 最大资源数 int n, m; // 进程数和资源数 int available[MAXM]; // 可用资源数 int maxn[MAXN][MAXM]; // 最大需求量矩阵 int allocation[MAXN][MAXM]; // 已分配资源矩阵 int need[MAXN][MAXM]; // 需求资源矩阵 bool finish[MAXN]; // 进程是否完成标记 int main() { // 输入进程数和资源数 cout << "请输入进程数和资源数: "; cin >> n >> m; // 输入可用资源数 cout << "请输入可用资源数: "; for (int i = 0; i < m; i++) { cin >> available[i]; } // 输入最大需求量矩阵 cout << "请输入最大需求量矩阵: " << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> maxn[i][j]; } } // 输入已分配资源矩阵 cout << "请输入已分配资源矩阵: " << endl; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { cin >> allocation[i][j]; need[i][j] = maxn[i][j] - allocation[i][j]; if (need[i][j] < 0) { // 如果已分配资源超过最大需求量,输入有误,退出程序 cout << "输入有误!已分配资源不能超过最大需求量!" << endl; return 0; } } } // 初始化进程未完成标记 for (int i = 0; i < n; i++) { finish[i] = false; } // 模拟银行家算法 int cnt = 0; vector<int> safe; // 安全序列 while (cnt < n) { bool flag = false; for (int i = 0; i < n; i++) { if (!finish[i]) { bool ok = true; for (int j = 0; j < m; j++) { if (need[i][j] > available[j]) { ok = false; break; } } if (ok) { // 如果进程 i 的需求可以得到满足 for (int j = 0; j < m; j++) { available[j] += allocation[i][j]; } finish[i] = true; safe.push_back(i); flag = true; cnt++; } } } if (!flag) { // 如果不存在一个进程的需求可以得到满足,说明系统不安全 cout << "系统不安全!" << endl; return 0; } } // 输出安全序列 cout << "安全序列为: "; for (int i = 0; i < safe.size(); i++) { cout << safe[i] << " "; } cout << endl; return 0; } ``` 这段代码实现了银行家算法的核心部分,通过输入进程数、资源数、可用资源数、最大需求量矩阵和已分配资源矩阵,模拟了银行家算法的执行过程,并输出了安全序列。需要注意的是,本算法是基于安全状态检测的,如果系统不安全,就会输出“系统不安全!”并退出程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值