当前位置: 首页 > news >正文

C++中锁和原子操作的区别及取舍

文章目录

    • 一、锁和原子操作的基本概念
      • (一)锁
      • (二)原子操作
    • 二、锁和原子操作的区别
      • (一)定义与实现
      • (二)性能特点
      • (三)适用场景
    • 三、锁和原子操作的应用场景图片示例
      • (一)C++中锁的应用场景
      • (二)C++中原子操作的应用场景
    • 四、如何取舍
      • (一)简单操作场景
      • (二)复杂操作场景

在现代多线程编程中,确保数据的一致性和线程的协调运行是至关重要的。C++为此提供了两种主要的同步机制:锁和原子操作。这两种机制在内存管理、执行效率和编程模式上有着本质的不同,但它们的共同目标是保证线程之间的正确数据共享和操作顺序。

一、锁和原子操作的基本概念

(一)锁

锁通常用于保护代码的临界区域,确保在同一时间内只有一个线程可以执行该区域内的代码。锁的类型多样,包括互斥锁(std::mutex)、递归锁(std::recursive_mutex)、读写锁(std::shared_mutex)等,各有其适用场景和特点。锁的使用简单直观,可以轻松保护复杂的数据结构或多步操作,但可能因为引入死锁、锁竞争和上下文切换等问题而影响程序的性能。

例如,使用互斥锁保护共享数据的示例代码如下:

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int shared_data = 0;void increment() {std::lock_guard<std::mutex> lock(mtx);++shared_data;std::cout << "Incremented: " << shared_data << std::endl;
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final shared data: " << shared_data << std::endl;return 0;
}

(二)原子操作

原子操作则提供了一种更为细粒度的同步方式,它可以保证对单个变量的操作是不可分割的,无需加锁即可完成。原子类型(如std::atomic<int>)确保了在多核处理器上的线程安全访问,通常用于计数器、标志位等简单数据的同步。原子操作能够显著减少同步的开销,特别是在无锁编程中,它们能提高程序的响应性和并行性能。

例如,使用原子操作实现计数器的示例代码如下:

#include <iostream>
#include <thread>
#include <atomic>std::atomic<int> counter(0);void increment() {for (int i = 0; i < 1000; ++i) {counter++;}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Counter value: " << counter << std::endl;return 0;
}

二、锁和原子操作的区别

(一)定义与实现

原子操作是指一个不可分割的操作,要么全部执行成功,要么全部不执行,不会出现中间状态。原子操作通常由硬件指令(如CAS,Compare-And-Swap)或编译器提供的原子类型(如C++的std::atomic)实现。

锁是一种同步机制,用于保护临界区,确保同一时间只有一个线程可以访问共享资源。锁通常基于操作系统提供的同步原语(如互斥锁、读写锁)或用户空间的实现(如自旋锁)。

(二)性能特点

原子操作通常由硬件指令直接支持,性能开销较低,且不会导致线程阻塞,适合高并发场景。但原子操作的适用范围有限,只能用于简单的操作(如加减、比较交换),复杂的操作无法直接实现。

锁的实现通常涉及操作系统调用(如futex),可能导致线程阻塞和上下文切换,性能开销较大。但锁的适用范围广,可以保护任意复杂的临界区,适用性更强。

(三)适用场景

原子操作适用于对单个变量进行简单的读写、加减、比较交换等操作,以及对性能要求较高的场景,如计数器、标志位等,同时适用于不能接受线程阻塞的场景。

锁适用于需要保护复杂临界区的场景,如对多个变量的操作、复杂的数据结构等,以及各种同步需求,如生产者 - 消费者模型、读写锁等,并且适用于可以接受线程阻塞的场景。

三、锁和原子操作的应用场景图片示例

(一)C++中锁的应用场景

C++中锁的应用场景1
这是一张流程图,展示了锁在分布式系统中的应用流程。从“开始”起,先“创建顺序子节点”,接着判断是否为最小节点,若是则“获取锁”,通过Watcher监听事件唤醒阻塞锁后“删除lock节点,释放锁”最后“结束”;若不是最小节点则“阻塞等待锁,并在上一节点注册watcher”,被通知后回到获取锁步骤。

(二)C++中原子操作的应用场景

C++中原子操作的应用场景1
这是一张表格图片,包含操作、triv、int type、ptr type、效果五列。操作列有诸如atomic a = val等多种操作;triv、int type、ptr type列在各操作对应行均为Yes;效果列详细说明了每个操作的效果,如atomic a = val是以val为a的初值等。表格内容为关于atomic相关操作及效果的说明。

四、如何取舍

(一)简单操作场景

如果需要对单个变量进行简单的操作(如加减、比较交换),并且对性能要求较高,优先选择原子操作。例如,实现一个简单的计数器:

#include <atomic>
#include <thread>
#include <iostream>std::atomic<int> counter(0);void increment() {for (int i = 0; i < 100000; ++i) {counter.fetch_add(1, std::memory_order_relaxed);}
}int main() {std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final value of counter: " << counter << std::endl;return 0;
}

(二)复杂操作场景

如果需要保护复杂的临界区或操作多个共享资源,优先选择锁。例如,保护一个复杂的数据结构:

#include <mutex>
#include <thread>
#include <list>
#include <iostream>std::mutex mtx;
std::list<int> myList;void insert(int value) {std::lock_guard<std::mutex> lock(mtx);myList.push_back(value);
}void remove(int value) {std::lock_guard<std::mutex> lock(mtx);myList.remove(value);
}int main() {std::thread t1(insert, 1);std::thread t2(insert, 2);std::thread t3(remove, 1);t1.join();t2.join();t3.join();for (int value : myList) {std::cout << value << " ";}std::cout << std::endl;return 0;
}

综上所述,在实际应用中,应根据具体需求选择合适的同步机制,以平衡性能和复杂性。

http://www.xdnf.cn/news/793387.html

相关文章:

  • 楼宇自控系统联动暖通空调:解密建筑环境舒适度提升路径
  • 域自适应 (Domain Adaptation,DA)基础
  • JS对数据类型的检测
  • TitanIDE智算版:一键开启云端算法开发环境
  • Servlet 生命周期
  • 高性能MCU的MPU与Cache优化详解
  • 线性动态规划
  • 张雪峰为9岁女儿申请40个左右商标!
  • 超声波粒度仪市场报告:行业现状、竞争格局与未来趋势分析
  • 原子操作与非原子操作
  • RTOS,其高级使用
  • TypeScript中class的两种继承方式extends和implements的对比
  • HTML5新特性
  • DAY 20 奇异值SVD分解
  • ant-design-vue select 下拉框不好用解决
  • Nginx 的配置文件
  • GCC内存占用统计使用指南
  • 【Android】双指旋转手势
  • AI 驱动工业:应用场景、挑战与未来趋势
  • SP网络结构:现代密码学的核心设计
  • SAP是什么?SAP概述
  • 免费论文查重与AI检测工具推荐
  • NVIDIA NVLink Fusion 是 PCIe Gen5 的 14 倍
  • pcie 日常问答-20250528
  • 累乘法求数列的通项公式
  • 手撕HashMap!(JDK7版本)
  • Unreal Niagara制作炫酷VJ粒子
  • 深入解析域名解析:原理、流程与应用实践
  • Spring 中创建 Bean 有几种方式?
  • Ajax技术深度解析:从原理到现代Web开发实践