欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > 现代C++多线程基础 - 避免死锁

现代C++多线程基础 - 避免死锁

2025/2/12 13:37:53 来源:https://blog.csdn.net/weixin_41733034/article/details/145582046  浏览:    关键词:现代C++多线程基础 - 避免死锁

避免死锁

文章目录

    • 避免死锁
      • 总览
      • 一把锁
        • 单一线程
          • std::recursive_mutex
          • 标注线程不安全
        • 多线程
      • 多把锁
        • 单一线程
        • 多线程
          • 上锁顺序相同
          • std::lock 同时上锁

总览

解决方法

  1. 防患未然:没有问题就不需要解决问题。

    1. 确定使用场景

      • 当前资源是否真的存在竞争,是否真的需要使用mutex。
        不需要的话就不要用。自然就没有死锁

      • 资源的所有权能不能简化,让 每个线程只持有一把锁。

    2. 使用者的RAII错误:从工具上避免

      忘记解锁 lock_guard,自由上/解锁unique_lock(更高的开销)

  2. 单一锁

    非多线程的问题:单一线程中的 单一锁 多次上锁。

    在函数A,函数B同时都需要用一把mutex锁上锁。函数A会调用到函数B

    1. 会锁死就干脆不要在函数里面用

      • 取消各个使用处的这个锁

        顾虑就是各个部分单独的使用,也都要加锁。更加的麻烦。

      • 写注释告诉使用者

        • 标注某个锁是线程不安全的。
        • 要求使用者必须在执行前为某个mutex上锁。
    2. std::recursive_mutex 允许同一线程多次使用一把锁

      如果同时需要 try_lock_for() 还有 std::recursive_timed_mutex

      • 自动判断是不是同一个线程 lock() 了多次同一个锁。

        如果是则

        • lock 计数器加1

        • unlock() 会让计数器减1,减到0时才真正解锁。

      • 相比普通的 std::mutex 有一定性能损失

  3. 多把锁

    • 单一线程

      • 永远不要在一个线程中同时持有多个锁 (锁一个,就释放一个)

        防患未然:没有问题就不需要解决问题。

    • 多线程

      • 在所有线程中,都按照相同的顺序上锁

      • std::lock std::scoped_lock

        直接同时对多个 mutex 上锁,不用关心顺序。

      • 操作系统(jvm C#虚拟机)检测死锁。选择性打破。

一把锁

单一线程

除了多线程相互持有两个锁会造成死锁外,同一线程多次使用一把锁也会死锁

  • 只有一个线程一个锁,如果 lock() 以后又调用 lock(),也会造成死锁。

但是有时候,我们确确实实的有如下需求:

如果要求这是一个连续的“原子的”操作,那么在函数A中调用函数B前,解锁mutex,执行结束后再上锁,这个解决方法就是不可行的。

  • 在函数A,函数B同时都需要用一把mutex锁上锁。
  • 函数A会调用到函数B
std::recursive_mutex

std::recursive_mutex

优点

  • 允许同一线程多次使用一把锁(会帮我们判断是否是同一线程)

  • 自动判断是不是同一个线程 lock() 了多次同一个锁,

    • 如果是则让计数器加1

    • 之后 unlock() 会让计数器减1,减到0时才真正解锁。

缺点

  • 相比普通的 std::mutex 有一定性能损失

同理还有 std::recursive_timed_mutex,如果你同时需要 try_lock_for() 的话。

标注线程不安全

会锁死就干脆不要在函数里面用这把锁,用在外面。

取消各个使用处的这个锁,写注释告诉使用者:

  • 标注某个锁是线程不安全的。
  • 要求使用者必须在执行前(一大串函数前)为某个mutex上锁。

顾虑:就是各个部分单独的使用,也都要加锁,非常的麻烦。

std::mutex mtx1;
/// NOTE: please lock mtx1 before calling other()
void other() {// do something
}void func() {mtx1.lock();other();mtx1.unlock();
}int main() {func();return 0;
}
多线程

没问题,一把锁多线程,不存在死锁。

多把锁

单一线程

防患未然:单一线程,永远不要同时持有两个锁 (锁一个,就释放一个)

int main() {std::mutex mtx1;std::mutex mtx2;std::thread t1([&] {for (int i = 0; i < 1000; i++) {mtx1.lock();mtx1.unlock();mtx2.lock();mtx2.unlock();}});std::thread t2([&] {for (int i = 0; i < 1000; i++) {mtx2.lock();mtx2.unlock();mtx1.lock();mtx1.unlock();}});t1.join();t2.join();return 0;
}
多线程
上锁顺序相同

复数个线程之间,由程序员保证多把锁的上锁顺序相同

int main() {std::mutex mtx1;std::mutex mtx2;std::thread t1([&] {for (int i = 0; i < 1000; i++) {mtx1.lock();mtx2.lock();mtx2.unlock();mtx1.unlock();}});std::thread t2([&] {for (int i = 0; i < 1000; i++) {mtx1.lock();mtx2.lock();mtx2.unlock();mtx1.unlock();}});t1.join();t2.join();return 0;
}
std::lock 同时上锁

std::lock 或 std::scoped_lock,支持同时对多把锁上锁

  • std::lock 可以同时对多个 mutex 上锁,
  • std::scoped_lock RAII 版本的std::lock。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com