C++并发编程基本概念
C++并发编程基本概念
前言
并发编程允许程序在多个线程中同时执行,可以显著提高程序的性能。
在多线程编程中,多个线程访问和修改共享数据时,如果没有正确的同步机制,就会出现数据竞争和不一致性的问题。
C++并发编程的一些基础概念:
- 互斥(Mutex):互斥用于确保一次只有一个线程可以访问共享数据,防止数据竞争。
- 条件变量(Condition Variable):条件变量用于线程间的同步,可以让线程在特定条件下等待或唤醒,通常与互斥量一起使用。
- 原子操作(Atomic Operations):原子操作是不可分割的操作,执行过程中不会被其他线程中断,不需要额外的同步机制。
- CAS(Compare-And-Swap,比较并交换)是一种原子操作,通常由硬件提供支持,用于实现多线程环境下的同步。CAS操作通常用于实现无锁数据结构(无锁队列、计数器、链表和集合等)和并发算法(自旋锁、读写锁、原子操作的计数等)。
1. 悲观锁(Pessimistic Lock)
悲观锁假定每次对共享资源的访问都有可能发生冲突,因此在访问资源前先锁定它。其他线程必须等待锁被释放才能继续访问。这种策略适用于高冲突的场景,因为它确保了对共享资源的访问是安全的。
悲观锁通常被实现为库级别的锁,封装了底层的操作系统同步原语(如互斥锁和信号量),这些原语涉及到内核态和用户态的切换,以及额外的线程调度和上下文切换开销,开销会高于硬件级别原子操作(CAS)。
互斥量(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 to " << shared_data << std::endl;
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
return 0;
}
2. 乐观锁(Optimistic Lock)
乐观锁假定对共享资源的访问冲突是很少发生的,因此不进行锁定,而是在操作完成后检查是否发生了冲突。如果检测到冲突,则重试操作。乐观锁适用于低冲突的场景,因为它在大多数情况下避免了锁定开销。
乐观锁依赖于Compare-And-Swap (CAS) 操作,CAS操作本身是一种硬件级别的原子操作,性能相对较高。
乐观锁需要注意ABA问题。
ABA问题发生在一个线程在检查某个值是否发生变化之前,该值被其他线程修改了两次,从而使得值表面上看起来没有变化,但实际上已经发生了变化。
解决ABA问题的方法:
- 使用版本号:每次对共享变量进行修改时,增加版本号。这样,即使变量的值回到了原始值,版本号也会不同。
- 使用指针和计数器:使用一个指针和一个计数器来表示一个共享资源的状态,每次修改资源时同时修改计数器。
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> shared_data(0);
void increment() {
int old_value;
int new_value;
do {
old_value = shared_data.load();
new_value = old_value + 1;
} while (!shared_data.compare_exchange_weak(old_value, new_value));
std::cout << "Incremented shared_data to " << new_value << std::endl;
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join();
t2.join();
return 0;
}
Reference:
原文地址:https://blog.csdn.net/itas109/article/details/140408893
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!