在多线程编程中,确保线程安全是保证程序正确性和稳定性的关键。以下是一些常见的线程安全方法及其应用场景。
原子值(Atomic Values)
原子值是简单类型的对齐读取和写入通常是原子的。例如,int32
、int64
类型的变量。
常见应用场景
- 双buffer切换时的指针赋值。
- 共享内存中简单原子类型的变量修改。
优势
- 性能高,且实现简单。
弊端
- 无锁且实现简单,但仅限于较为简单的数据类型。
示例代码:使用 std::atomic
进行原子操作
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> counter(0); // 定义原子变量
void increment() {
for (int i = 0; i < 1000; ++i) {
.fetch_add(1, std::memory_order_relaxed); // 原子增加
counter}
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
.join();
t1.join();
t2
std::cout << "Final counter value: " << counter.load() << std::endl; // 输出最终计数
return 0;
}
简单原子变量(Atomic Variable)
通过编译器、语言等实现原子的 CPU 指令。
- 原子类型的自增自减和立即赋值等,不强调调用后只要求最终结果的正确。
优势
- 性能高,且实现简单。
弊端
- 可能存在竞争较严重的时刻,自旋可能非常浪费 CPU。
示例代码:使用 std::atomic
进行自增操作
#include <iostream>
#include <atomic>
std::atomic<int> value(0);
void add_value() {
for (int i = 0; i < 100; ++i) {
.fetch_add(1, std::memory_order_relaxed); // 原子自增
value}
}
int main() {
std::thread t1(add_value);
std::thread t2(add_value);
.join();
t1.join();
t2
std::cout << "Final value: " << value.load() << std::endl;
return 0;
}
CAS(Compare-And-Swap)
CAS 是一种常见的原子操作,通常用于实现内存屏障,确保线程安全。
常见应用场景
- 内存屏障中的关键操作。
优势
- 性能高,且实现简单。
弊端
- 对执行的先后顺序有较严格的要求。
示例代码:使用 std::atomic_compare_exchange_strong
实现 CAS
#include <iostream>
#include <atomic>
std::atomic<int> data(0);
void cas_test() {
int expected = 0;
int desired = 1;
if (data.compare_exchange_strong(expected, desired)) {
std::cout << "CAS succeeded, data set to: " << data.load() << std::endl;
} else {
std::cout << "CAS failed, data is still: " << data.load() << std::endl;
}
}
int main() {
std::thread t1(cas_test);
std::thread t2(cas_test);
.join();
t1.join();
t2return 0;
}
双buffer(Double Buffering)
简介
双buffer是一种在内存中保持两个缓冲区的技术,通常用于减少延迟。
常见应用场景
- 双buffer切换时的指针赋值。
优势
- 性能高,且实现简单。
弊端
- 浪费内存空间。
示例代码:使用双缓冲技术
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
std::vector<int> buffer1(1000), buffer2(1000); // 两个缓冲区
std::atomic<bool> switch_buffer(false); // 缓冲区切换标志
void fill_buffer(std::vector<int>& buffer) {
for (int i = 0; i < 1000; ++i) {
[i] = i;
buffer}
}
void process_buffer() {
while (true) {
if (!switch_buffer.load()) {
(buffer1);
fill_buffer.store(true);
switch_buffer} else {
(buffer2);
fill_buffer.store(false);
switch_buffer}
}
}
int main() {
std::thread t1(process_buffer);
.join();
t1return 0;
}
延迟删除双buffer(Double Buffering with Deferred Deletion)
简介
在更新后短期内使用双buffer,随后删除旧版本,并通过指针赋值的原子操作切换到新数据。
常见应用场景
- 更新频繁的数据。
优势
- 性能高,无需加锁。
弊端
- 更新频率有限制。
示例代码:使用延迟删除
#include <iostream>
#include <atomic>
#include <thread>
#include <vector>
std::vector<int> buffer1(1000), buffer2(1000);
std::atomic<bool> use_buffer1(true); // 使用哪个缓冲区
void fill_buffer(std::vector<int>& buffer) {
for (int i = 0; i < 1000; ++i) {
[i] = i;
buffer}
}
void process_buffer() {
while (true) {
if (use_buffer1.load()) {
(buffer1);
fill_buffer.store(false); // 切换到buffer2
use_buffer1} else {
(buffer2);
fill_buffer.store(true); // 切换到buffer1
use_buffer1}
}
}
int main() {
std::thread t1(process_buffer);
.join();
t1return 0;
}