0

0

C++如何使用mutex保证内存可见性

P粉602998670

P粉602998670

发布时间:2025-09-17 10:13:01

|

780人浏览过

|

来源于php中文网

原创

std::mutex通过acquire-release语义建立happens-before关系,确保线程间内存可见性:当一个线程释放锁时,其对共享数据的修改会写回主内存;另一个线程获取同一互斥量时,能读取到最新值,防止重排序与缓存不一致问题。

c++如何使用mutex保证内存可见性

C++中,

std::mutex
主要通过建立“happens-before”关系来保证内存可见性。当一个线程解锁(release)一个互斥量时,它在该互斥量保护区域内对内存的所有修改都会被“同步”到主内存。随后,当另一个线程成功锁定(acquire)同一个互斥量时,它会“看到”之前解锁线程所做的所有内存修改。这确保了共享数据的一致性视图,防止了编译器和CPU的重排序优化破坏多线程程序的正确性。

解决方案

要理解

std::mutex
如何保证内存可见性,我们需要深入C++内存模型(C++ Memory Model)和“happens-before”关系的精髓。这不仅仅是关于锁定和解锁那么简单,它更像是一种契约,编译器和硬件必须遵守的契约。

想象一下,你有一个共享变量

data
和一个布尔标志
ready
。线程A负责计算
data
并设置
ready
true
,线程B则等待
ready
true
后使用
data
。如果没有
mutex
,可能会发生什么?

  • 编译器重排序: 编译器为了优化性能,可能会将线程A中对
    data
    的写入操作排在对
    ready
    的写入操作之后。甚至,如果
    ready
    被设置为
    true
    ,但
    data
    还没完全写入,线程B就可能读到旧的或不完整的数据。
  • CPU重排序: 处理器也有自己的乱序执行机制。即使编译器没有重排,CPU也可能在执行指令时,将对
    data
    的写入延迟,而先处理对
    ready
    的写入,导致类似的问题。
  • 缓存一致性问题: 每个CPU核心都有自己的缓存。线程A在核心1上修改了
    data
    ready
    ,这些修改可能只存在于核心1的缓存中,并没有立即写回主内存。线程B在核心2上运行时,如果直接从核心2的缓存读取,它可能看到的是旧的值。

std::mutex
正是为了解决这些问题而生。它的工作机制可以概括为:

立即学习C++免费学习笔记(深入)”;

  • Acquire 操作 (
    lock()
    ):
    当一个线程调用
    mutex::lock()
    时,它执行一个“acquire”操作。这个操作会确保所有在
    lock()
    之后发生的内存访问,都不能被重排到
    lock()
    之前。更重要的是,它会确保当前线程的本地缓存与主内存同步,读取到其他线程在
    mutex
    保护下写入的最新数据。这通常涉及CPU的内存屏障(memory barrier)指令,强制刷新或失效缓存。
  • Release 操作 (
    unlock()
    ):
    当一个线程调用
    mutex::unlock()
    时,它执行一个“release”操作。这个操作会确保所有在
    unlock()
    之前发生的内存访问,都不能被重排到
    unlock()
    之后。同时,它会强制将当前线程在
    mutex
    保护下对内存的所有修改从本地缓存写回主内存,使其对其他处理器可见。

所以,当线程A在持有

mutex
时修改了
data
ready
,并在释放
mutex
时,这些修改被保证会写回主内存。当线程B成功获取了同一个
mutex
时,它会强制从主内存中读取最新的
data
ready
值,而不是从其可能过期的本地缓存中读取。这种“先释放后获取”的顺序,在C++内存模型中建立了一个强大的“happens-before”关系链,从而保证了内存可见性。

WPS AI
WPS AI

金山办公发布的AI办公应用,提供智能文档写作、阅读理解和问答、智能人机交互的能力。

下载
#include 
#include 
#include 
#include 
#include  // For std::this_thread::sleep_for

std::vector shared_data;
std::mutex mtx;
bool data_ready = false; // 共享标志

void producer_thread() {
    // 模拟一些计算耗时
    std::this_thread::sleep_for(std::chrono::milliseconds(100));

    // 锁定互斥量,开始修改共享数据
    mtx.lock();
    try {
        std::cout << "Producer: Adding data..." << std::endl;
        for (int i = 0; i < 5; ++i) {
            shared_data.push_back(i * 10);
        }
        data_ready = true; // 设置标志
        std::cout << "Producer: Data added and ready flag set." << std::endl;
    } catch (...) {
        mtx.unlock(); // 确保异常安全解锁
        throw;
    }
    mtx.unlock(); // 释放互斥量
}

void consumer_thread() {
    // 等待数据准备好
    // 注意:这里用一个简单的循环来演示,实际生产中会用条件变量
    // 但为了突出mutex的可见性,这里先简化
    while (true) {
        mtx.lock(); // 尝试获取互斥量
        if (data_ready) {
            std::cout << "Consumer: Data is ready. Reading data..." << std::endl;
            for (int val : shared_data) {
                std::cout << val << " ";
            }
            std::cout << std::endl;
            mtx.unlock(); // 释放互斥量
            break; // 读取完毕,退出循环
        }
        mtx.unlock(); // 释放互斥量,以便生产者可以获取
        std::this_thread::sleep_for(std::chrono::milliseconds(10)); // 避免忙等
    }
}

int main() {
    std::thread producer(producer_thread);
    std::thread consumer(consumer_thread);

    producer.join();
    consumer.join();

    std::cout << "Main: All threads finished." << std::endl;

    return 0;
}

在这个例子中,当

producer_thread
调用
mtx.unlock()
时,
shared_data
data_ready
的所有修改都会被保证写回主内存。当
consumer_thread
成功调用
mtx.lock()
时,它被保证能看到这些最新的修改。如果没有
mutex
consumer_thread
可能会在
data_ready
true
时,仍然读取到空的或不完整的
shared_data
,这就是内存可见性问题。

为什么单独的原子操作不足以保证复杂场景的内存可见性?

有时候,人们会觉得,既然C++11引入了

std::atomic
,并且它也能提供内存同步,那是不是就可以完全替代
mutex
来解决可见性问题了呢?答案是:不完全是。
std::atomic
确实在单个变量的读写操作上提供了强大的内存可见性保证,比如
std::atomic
std::atomic
,它们可以确保对这些原子变量的修改能被其他线程及时看到,并防止相关的重排序。

然而,当涉及到多个变量之间的数据一致性时,单独的原子操作就显得力不从心了。

std::atomic
保证的是对其自身操作的原子性和可见性,但它无法保证一组非原子操作或者多个原子操作之间的原子性和可见性。

举个例子,如果线程A需要修改

data_a
data_b
两个变量,并且这两个修改必须作为一个不可分割的整体被其他线程看到。如果线程A先修改了
data_a
(原子操作),然后修改了
data_b
(原子操作),在两次修改之间,线程B可能会看到
data_a
的新值和
data_b
的旧值,这导致了数据不一致。
std::atomic
本身无法将这两个独立的原子操作“捆绑”起来。

std::mutex
则不同,它提供的是一个临界区(critical section)的概念。一旦一个线程成功锁定了
mutex
,它就独占了对该
mutex
保护的资源的访问权。在这个临界区内,无论你对多少个变量进行修改,这些修改在
mutex
释放时都会被作为一个整体同步到主内存

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

544

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

113

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

200

2025.08.29

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

523

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

186

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

15

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

15

2026.01.21

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

7

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 8万人学习

C 教程
C 教程

共75课时 | 4.3万人学习

C++教程
C++教程

共115课时 | 14.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号