C++线程安全问题详解

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 设计原则

  1. 优先使用不可变数据
  2. 限制共享状态的范围
  3. 使用消息传递而非共享内存
  4. 遵循RAII原则管理锁
  5. 保持锁的持有时间尽可能短

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++应用程序,避免常见的并发问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值