You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不同函数中使用Mutex的Lock与Unlock?解决死锁问题

问题描述

我有多个按顺序执行的函数,第一个函数确定RAM中某个位置的指针,若该位置被其他线程共享则调用Mutex的lock函数;第二个函数处理指针指向的数据,之后解锁资源。但程序执行mtx.lock()时就会卡住,无法继续运行。

我正在模拟CPU和简单图形芯片,尝试锁定CPU与GPU(分属不同线程)共同访问的内存地址,以防止两者同时访问这些内存位置。目前我为虚拟内存数组中的每个地址或地址范围都设置了Mutex,但仅在CPU线程中实现了锁,GPU端尚未实现,理论上CPU首次执行lock时不会被锁。

更具体地说,CPU运行在独立线程,GPU(含显示输出)运行在主线程,我未调用thread_cpu.join()(线程初始化方式为std::thread thread_cpu(cpu.run());),不确定这是否是问题所在。

相关代码如下:

sync.h(被多个cpp文件包含)

inline std::mutex mtx;

memory.cpp(返回指针并锁定Mutex,处理完指针指向的数据后由post_process解锁)

#include "sync.h"

class Memory{
private:
     UInt32* virtual_memory;

public:
     Memory(){
          virtual_memory = new UInt32[0x2000];
     };

     UInt32* get_address(UInt32 addr_virt){
          mtx.lock();
          return &virtual_memory[addr_virt];
     }

     void post_process(){
          mtx.unlock();
     }
}

实际代码更复杂,但上述代码已体现Mutex的实现方式。请问如何实现该功能并解决当前问题?

问题分析与解决方案

1. 全局Mutex的定义问题

当前用inline std::mutex mtx;在头文件定义全局Mutex,可能导致每个包含该头文件的编译单元生成独立的Mutex实例——这意味着CPU线程和主线程(GPU)操作的是不同的锁,完全起不到同步作用,甚至可能因为某个编译单元内的锁未解锁,导致该单元后续的lock调用卡住。

修复方式:

  • 在sync.h中声明Mutex为外部变量:
extern std::mutex mtx;
  • 在单独的实现文件(比如sync.cpp)中定义Mutex,确保整个程序只有一个实例:
#include "sync.h"
std::mutex mtx;

2. 手动锁/解锁的风险

当前设计依赖调用方在get_address后必须调用post_process解锁,一旦出现函数提前返回、异常抛出等情况,Mutex会被永久锁定,后续所有lock调用都会卡住。

修复方式:使用RAII(资源获取即初始化)机制管理锁,确保自动解锁:

方案A:封装读写操作(推荐)

直接提供内存读写接口,内部用std::lock_guard自动管理锁:

#include "sync.h"
#include <mutex>

class Memory{
private:
     UInt32* virtual_memory;

public:
     Memory(){
          virtual_memory = new UInt32[0x2000];
     };

     UInt32 read(UInt32 addr_virt){
          std::lock_guard<std::mutex> lock(mtx);
          return virtual_memory[addr_virt];
     }

     void write(UInt32 addr_virt, UInt32 value){
          std::lock_guard<std::mutex> lock(mtx);
          virtual_memory[addr_virt] = value;
     }
};

方案B:返回带锁的对象(需直接操作指针时)

如果必须直接操作内存指针,返回std::unique_lock对象,让锁在对象销毁时自动释放:

#include "sync.h"
#include <mutex>

class Memory{
private:
     UInt32* virtual_memory;

public:
     Memory(){
          virtual_memory = new UInt32[0x2000];
     };

     std::unique_lock<std::mutex> get_locked_access(UInt32 addr_virt, UInt32** out_ptr){
          std::unique_lock<std::mutex> lock(mtx);
          *out_ptr = &virtual_memory[addr_virt];
          return lock;
     }
};

调用示例:

UInt32* ptr;
auto lock = memory.get_locked_access(0x100, &ptr);
// 操作ptr指向的数据
// lock销毁时自动解锁,即使中途抛出异常也不会遗漏

3. 线程管理问题

虽然未调用thread_cpu.join()不是lock卡住的直接原因,但主线程结束时若子线程(CPU线程)仍在运行,会导致程序异常终止,行为不可控。必须在主线程合适的位置(比如程序退出前)调用thread_cpu.join(),等待CPU线程执行完毕。

4. 死锁排查步骤

  • 检查CPU线程中是否存在调用get_address后未调用post_process的场景(比如分支提前返回、异常),这会导致Mutex永久锁定。
  • 确认主线程(GPU)是否真的没有操作该Mutex,若主线程某处意外调用了mtx.lock()且未解锁,会导致CPU线程的lock调用卡住。

内容的提问来源于stack exchange,提问作者Math Merkl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 01:47:20