C++线程安全问题详解
1. 线程安全基础概念
1.1 什么是线程安全
线程安全是指代码在多个线程并发执行时,仍然能够正确工作,不会出现数据竞争、死锁等问题。
#include <iostream>
#include <thread>
#include <vector>
class UnsafeCounter {
int value = 0;
public:
void increment() {
++value; // 非原子操作,线程不安全
}
int get() const { return value; }
};
void demonstrate_race_condition() {
UnsafeCounter counter;
std::vector<std::thread> threads;
for (int i = 0; i < 10; ++i) {
threads.emplace_back([&counter] {
for (int j = 0; j < 1000; ++j) {
counter.increment();
}
});
}
for (auto& t : threads) t.join();
// 结果可能小于10000,因为存在数据竞争
std::cout << "Final value: " << counter.get() << std::endl;
}
2. 常见的线程安全问题
2.1 数据竞争(Data Race)
class DataRaceExample {
std::vector<int> data;
public:
// 线程不安全的接口
void add_item(int item) {
data.push_back(item); // 可能引发数据竞争
}
void process_items() {
for (auto& item : data) { // 可能同时被修改
process(item);
}
}
// 更危险的例子:条件竞争
bool contains(int item) const {
return std::find(data.begin(), data.end(), item) != data.end();
}
void add_if_missing(int item) {
if (!contains(item)) { // 检查
data.push_back(item); // 操作 - 非原子!
}
}
};
2.2 死锁(Deadlock)
class DeadlockExample {
std::mutex mutex1, mutex2;
int resource1 = 0, resource2 = 0;
public:
void method1() {
std::lock_guard<std::mutex> lock1(mutex1); // 获取锁1
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock2(mutex2); // 等待锁2 - 可能死锁
++resource1;
++resource2;
}
void method2() {
std::lock_guard<std::mutex> lock2(mutex2); // 获取锁2
std::this_thread::sleep_for(std::chrono::milliseconds(10));
std::lock_guard<std::mutex> lock1(mutex1); // 等待锁1 - 可能死锁
--resource1;
--resource2;
}
};
void create_deadlock() {
DeadlockExample example;
std::thread t1([&] { example.method1(); });
std::thread t2([&] { example.method2(); });
t1.join();
t2.join(); // 可能永远阻塞
}
2.3 活锁(Livelock)
class LivelockExample {
std::mutex mutex1, mutex2;
bool worker1_trying = false;
bool worker2_trying = false;
public:
void worker1() {
while (true) {
worker1_trying = true;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (!worker2_trying) {
std::lock_guard<std::mutex> lock1(mutex1);
// 做一些工作...
worker1_trying = false;
break;
} else {
worker1_trying = false;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
void worker2() {
while (true) {
worker2_trying = true;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (!worker1_trying) {
std::lock_guard<std::mutex> lock2(mutex2);
// 做一些工作...
worker2_trying = false;
break;
} else {
worker2_trying = false;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
}
};
2.4 资源饥饿(Starvation)
class StarvationExample {
std::mutex resource_mutex;
std::mutex low_priority_mutex;
std::mutex high_priority_mutex;
public:
void low_priority_worker() {
while (true) {
std::lock_guard<std::mutex> lock1(low_priority_mutex);
std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 长时间工作
std::lock_guard<std::mutex> lock2(resource_mutex);
// 访问共享资源
}
}
void high_priority_worker() {
while (true) {
std::lock_guard<std::mutex> lock1(high_priority_mutex);
std::lock_guard<std::mutex> lock2(resource_mutex); // 可能被低优先级阻塞
// 高优先级任务被阻塞
}
}
};
3. 同步原语和解决方案
3.1 互斥锁(Mutex)
class SafeCounter {
int value = 0;
mutable std::mutex mutex;
public:
void increment() {
std::lock_guard<std::mutex> lock(mutex); // RAII保护
++value;
}
int get() const {
std::lock_guard<std::mutex> lock(mutex);
return value;
}
// 更复杂的操作也需要保护
bool compare_and_swap(int expected, int new_value) {
std::lock_guard<std::mutex> lock(mutex);
if (value == expected) {
value = new_value;
return true;
}
return false;
}
};
3.2 读写锁(Read-Write Lock)
#include <shared_mutex>
class ThreadSafeConfig {
std::unordered_map<std::string, std::string> config;
mutable std::shared_mutex rw_mutex; // C++17
public:
// 读操作 - 多个线程可以同时读取
std::string get(const std::string& key) const {
std::shared_lock lock(rw_mutex); // 共享锁
auto it = config.find(key);
return it != config.end() ? it->second : "";
}
bool contains(const std::string& key) const {
std::shared_lock lock(rw_mutex);
return config.find(key) != config.end();
}
// 写操作 - 独占访问
void set(const std::string& key, const std::string& value) {
std::unique_lock lock(rw_mutex); // 独占锁
config[key] = value;
}
void remove(const std::string& key) {
std::unique_lock lock(rw_mutex);
config.erase(key);
}
};
3.3 原子操作
#include <atomic>
class AtomicCounter {
std::atomic<int> value{0};
public:
void increment() {
++value; // 原子操作,无需锁
}
int get() const {
return value.load(std::memory_order_acquire);
}
// 复杂的原子操作
bool try_increment_until(int limit) {
int current = value.load(std::memory_order_relaxed);
while (current < limit) {
if (value.compare_exchange_weak(current, current + 1,
std::memory_order_release,
std::memory_order_relaxed)) {
return true;
}
}
return false;
}
};
// 无锁数据结构示例
template<typename T>
class LockFreeStack {
private:
struct Node {
T data;
Node* next;
Node(const T& data) : data(data), next(nullptr) {}
};
std::atomic<Node*> head{nullptr};
public:
void push(const T& data) {
Node* new_node = new Node(data);
new_node->next = head.load(std::memory_order_relaxed);
while (!head.compare_exchange_weak(new_node->next, new_node,
std::memory_order_release,
std::memory_order_relaxed)) {
// CAS失败,重试
}
}
bool pop(T& result) {
Node* old_head = head.load(std::memory_order_relaxed);
while (old_head &&
!head.compare_exchange_weak(old_head, old_head->next,
std::memory_order_acquire,
std::memory_order_relaxed)) {
// CAS失败,重试
}
if (!old_head) return false;
result = std::move(old_head->data);
delete old_head;
return true;
}
};
3.4 条件变量
class ThreadSafeQueue {
private:
std::queue<int> queue;
mutable std::mutex mutex;
std::condition_variable cond_var;
bool shutdown = false;
public:
void push(int value) {
{
std::lock_guard lock(mutex);
queue.push(value);
}
cond_var.notify_one(); // 通知一个等待的消费者
}
bool pop(int& value) {
std::unique_lock lock(mutex);
// 等待条件:队列非空或关闭
cond_var.wait(lock, [this] {
return !queue.empty() || shutdown;
});
if (shutdown && queue.empty()) {
return false; // 关闭且无数据
}
value = queue.front();
queue.pop();
return true;
}
void shutdown_queue() {
{
std::lock_guard lock(mutex);
shutdown = true;
}
cond_var.notify_all(); // 通知所有等待的线程
}
size_t size() const {
std::lock_guard lock(mutex);
return queue.size();
}
};
4. 高级同步模式
4.1 生产者-消费者模式
class ProducerConsumer {
std::queue<std::function<void()>> tasks;
std::mutex mutex;
std::condition_variable cond_var;
std::vector<std::thread> workers;
bool stop = false;
public:
ProducerConsumer(size_t num_workers) {
for (size_t i = 0; i < num_workers; ++i) {
workers.emplace_back([this] { worker_loop(); });
}
}
~ProducerConsumer() {
{
std::lock_guard lock(mutex);
stop = true;
}
cond_var.notify_all();
for (auto& worker : workers) {
if (worker.joinable()) worker.join();
}
}
template<typename F>
void submit(F&& task) {
{
std::lock_guard lock(mutex);
tasks.emplace(std::forward<F>(task));
}
cond_var.notify_one();
}
private:
void worker_loop() {
while (true) {
std::function<void()> task;
{
std::unique_lock lock(mutex);
cond_var.wait(lock, [this] {
return stop || !tasks.empty();
});
if (stop && tasks.empty()) {
return;
}
task = std::move(tasks.front());
tasks.pop();
}
try {
task(); // 执行任务
} catch (const std::exception& e) {
std::cerr << "Task failed: " << e.what() << std::endl;
}
}
}
};
4.2 屏障(Barrier)
#include <barrier> // C++20
class ParallelProcessor {
std::barrier<> sync_point;
std::vector<std::thread> workers;
std::vector<int> data;
std::atomic<bool> done{false};
public:
ParallelProcessor(size_t num_workers, size_t data_size)
: sync_point(num_workers), data(data_size)
{
for (size_t i = 0; i < num_workers; ++i) {
workers.emplace_back([this, i, num_workers] {
worker_function(i, num_workers);
});
}
}
~ParallelProcessor() {
done = true;
for (auto& worker : workers) {
if (worker.joinable()) worker.join();
}
}
private:
void worker_function(size_t worker_id, size_t total_workers) {
const size_t chunk_size = data.size() / total_workers;
const size_t start = worker_id * chunk_size;
const size_t end = (worker_id == total_workers - 1)
? data.size()
: start + chunk_size;
while (!done) {
// 阶段1:处理数据
for (size_t i = start; i < end; ++i) {
data[i] = process_item(data[i]);
}
// 等待所有worker完成阶段1
sync_point.arrive_and_wait();
// 阶段2:交换边界数据
if (worker_id > 0) {
exchange_boundary_data(worker_id - 1, worker_id);
}
// 等待所有worker完成阶段2
sync_point.arrive_and_wait();
}
}
int process_item(int item) { return item * 2; }
void exchange_boundary_data(size_t left, size_t right) {
// 交换边界数据
}
};
4.3 信号量(C++20)
#include <semaphore>
class ResourcePool {
std::vector<std::string> resources;
std::counting_semaphore<> available;
std::mutex resource_mutex;
public:
ResourcePool(const std::vector<std::string>& initial_resources)
: resources(initial_resources)
, available(initial_resources.size())
{}
std::string acquire() {
available.acquire(); // 等待资源可用
std::lock_guard lock(resource_mutex);
auto resource = std::move(resources.back());
resources.pop_back();
return resource;
}
void release(std::string resource) {
{
std::lock_guard lock(resource_mutex);
resources.push_back(std::move(resource));
}
available.release(); // 通知资源可用
}
};
5. 死锁预防和解决
5.1 锁顺序策略
class DeadlockFreeSystem {
std::mutex mutex1, mutex2, mutex3;
// 定义锁的获取顺序
enum LockOrder { FIRST = 1, SECOND = 2, THIRD = 3 };
class OrderedLock {
std::mutex& mutex;
LockOrder order;
static thread_local LockOrder current_order;
public:
OrderedLock(std::mutex& m, LockOrder o) : mutex(m), order(o) {
if (order <= current_order) {
throw std::logic_error("Lock order violation!");
}
mutex.lock();
current_order = order;
}
~OrderedLock() {
mutex.unlock();
current_order = static_cast<LockOrder>(static_cast<int>(order) - 1);
}
};
public:
void safe_operation1() {
OrderedLock lock1(mutex1, FIRST);
OrderedLock lock2(mutex2, SECOND);
// 安全操作
}
void safe_operation2() {
OrderedLock lock1(mutex1, FIRST);
OrderedLock lock3(mutex3, THIRD);
// 安全操作
}
};
5.2 使用std::lock同时获取多个锁
class MultiLockExample {
std::mutex mutex1, mutex2;
int data1 = 0, data2 = 0;
public:
void transfer_data() {
// 使用std::lock同时获取多个锁,避免死锁
std::unique_lock lock1(mutex1, std::defer_lock);
std::unique_lock lock2(mutex2, std::defer_lock);
std::lock(lock1, lock2); // 原子性地获取两个锁
// 安全地操作两个受保护的数据
int temp = data1;
data1 = data2;
data2 = temp;
}
void complex_operation() {
std::scoped_lock lock(mutex1, mutex2); // C++17,更简洁
// 同时持有两个锁
data1 += data2;
data2 -= data1;
}
};
5.3 超时锁和尝试锁
class TimeoutExample {
std::timed_mutex mutex1, mutex2;
public:
bool try_complex_operation(std::chrono::milliseconds timeout) {
// 尝试在超时时间内获取锁
std::unique_lock lock1(mutex1, std::defer_lock);
if (!lock1.try_lock_for(timeout)) {
return false; // 获取第一个锁超时
}
std::unique_lock lock2(mutex2, std::defer_lock);
if (!lock2.try_lock_for(timeout)) {
return false; // 获取第二个锁超时
}
// 成功获取两个锁,执行操作
perform_operation();
return true;
}
bool try_lock_all() {
// 尝试立即获取所有锁
std::scoped_lock lock(mutex1, mutex2, std::try_to_lock);
return lock.owns_lock(); // 检查是否成功获取所有锁
}
private:
void perform_operation() {
// 复杂的操作
}
};
6. 线程安全的设计模式
6.1 线程局部存储
class ThreadLocalExample {
// 每个线程有自己的实例
static thread_local int thread_specific_data;
static std::atomic<int> global_counter{0};
public:
void process() {
// 初始化线程局部数据
if (thread_specific_data == 0) {
thread_specific_data = global_counter.fetch_add(1) + 1;
}
// 使用线程局部数据,无需同步
std::cout << "Thread " << std::this_thread::get_id()
<< " has data: " << thread_specific_data << std::endl;
}
};
// 定义线程局部变量
thread_local int ThreadLocalExample::thread_specific_data = 0;
6.2 不可变对象模式
class ImmutableConfig {
const std::unordered_map<std::string, std::string> settings;
public:
ImmutableConfig(std::unordered_map<std::string, std::string> init_settings)
: settings(std::move(init_settings))
{}
// 所有方法都是const,线程安全
std::string get(const std::string& key) const {
auto it = settings.find(key);
return it != settings.end() ? it->second : "";
}
bool contains(const std::string& key) const {
return settings.find(key) != settings.end();
}
// 创建新版本而不是修改现有对象
ImmutableConfig with_setting(const std::string& key, const std::string& value) const {
auto new_settings = settings;
new_settings[key] = value;
return ImmutableConfig(std::move(new_settings));
}
};
6.3 写时复制(Copy-on-Write)
template<typename T>
class CopyOnWrite {
mutable std::shared_ptr<const T> data;
mutable std::mutex mutex;
public:
CopyOnWrite(T&& initial_data)
: data(std::make_shared<const T>(std::move(initial_data)))
{}
// 读操作 - 线程安全
std::shared_ptr<const T> read() const {
std::lock_guard lock(mutex);
return data;
}
// 写操作 - 创建副本
template<typename Modifier>
void modify(Modifier&& modifier) {
std::lock_guard lock(mutex);
auto new_data = std::make_shared<T>(*data); // 创建副本
modifier(*new_data); // 修改副本
data = std::move(new_data); // 原子性地替换
}
};
// 使用示例
void use_copy_on_write() {
CopyOnWrite<std::vector<int>> cow_vector(std::vector<int>{1, 2, 3});
// 多个线程可以同时读取
auto reader = [&cow_vector] {
auto data = cow_vector.read();
for (int val : *data) {
std::cout << val << " ";
}
std::cout << std::endl;
};
// 修改时创建副本
cow_vector.modify([](std::vector<int>& vec) {
vec.push_back(4);
});
}
7. 测试和调试线程安全
7.1 线程安全测试
#include <gtest/gtest.h>
class ThreadSafeTest : public ::testing::Test {
protected:
template<typename T>
void stress_test(T& object, int num_threads, int operations_per_thread) {
std::vector<std::thread> threads;
std::atomic<bool> start{false};
for (int i = 0; i < num_threads; ++i) {
threads.emplace_back([&, i] {
while (!start) { std::this_thread::yield(); } // 等待开始信号
for (int j = 0; j < operations_per_thread; ++j) {
if (i % 2 == 0) {
object.increment();
} else {
object.decrement();
}
}
});
}
start = true; // 同时启动所有线程
for (auto& t : threads) t.join();
// 验证最终状态
ASSERT_EQ(object.get(), 0);
}
};
TEST_F(ThreadSafeTest, CounterStressTest) {
SafeCounter counter;
stress_test(counter, 10, 10000);
}
7.2 使用ThreadSanitizer
# 使用ThreadSanitizer编译
g++ -fsanitize=thread -g -O1 program.cpp -o program
# 运行会自动检测数据竞争
./program
8. 最佳实践总结
8.1 设计原则
- 优先使用不可变数据
- 限制共享状态的范围
- 使用消息传递而非共享内存
- 遵循RAII原则管理锁
- 保持锁的持有时间尽可能短
8.2 代码示例:线程安全的日志系统
class ThreadSafeLogger {
std::ofstream log_file;
mutable std::shared_mutex file_mutex;
std::atomic<bool> enabled{true};
public:
ThreadSafeLogger(const std::string& filename) {
log_file.open(filename, std::ios::app);
}
void log(const std::string& message) {
if (!enabled) return;
auto timestamp = std::chrono::system_clock::now();
std::string full_message = format_message(timestamp, message);
{
std::shared_lock lock(file_mutex); // 多个线程可以同时写
if (log_file.is_open()) {
log_file << full_message << std::endl;
}
}
}
void rotate_log(const std::string& new_filename) {
std::unique_lock lock(file_mutex); // 独占访问
if (log_file.is_open()) {
log_file.close();
}
log_file.open(new_filename, std::ios::app);
}
void shutdown() {
enabled = false;
std::unique_lock lock(file_mutex);
if (log_file.is_open()) {
log_file.close();
}
}
private:
std::string format_message(const auto& timestamp, const std::string& message) {
auto time_t = std::chrono::system_clock::to_time_t(timestamp);
std::stringstream ss;
ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
return "[" + ss.str() + "] " + message;
}
};
通过遵循这些模式和实践,可以构建出健壮的线程安全C++应用程序,避免常见的并发问题。
1591

被折叠的 条评论
为什么被折叠?



