什么是线程安全?

什么是线程安全?

线程安全 指的是在多线程环境中,当多个线程同时访问同一资源 (变量、数据结构、对象等)时,程序能够保证数据的一致性 和正确性 ,不会出现数据损坏 或逻辑错误。

一、非线程安全的例子:

cpp 复制代码
#include <iostream>
#include <thread>

int counter = 0;  // 共享资源

void increment() {
    for (int i = 0; i < 100000; ++i) {
        counter++;  // 非原子操作,可能产生数据竞争
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    // 结果可能不是200000
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}

二、实现线程安全的常见方法(C++示例)

1. 互斥锁 (Mutex)
cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>

std::mutex mtx;
int counter = 0;

void increment() {
    for (int i = 0; i < 100000; ++i) {
        std::lock_guard<std::mutex> lock(mtx);  // 自动加锁解锁
        counter++;
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    std::cout << "Counter: " << counter << std::endl;  // 总是200000
    return 0;
}
2. 原子操作 (Atomic)
cpp 复制代码
#include <iostream>
#include <thread>
#include <atomic>

std::atomic<int> counter(0);  // 原子变量

void increment() {
    for (int i = 0; i < 100000; ++i) {
        counter.fetch_add(1, std::memory_order_relaxed);
        // 或 counter++ (C++11保证atomic的自增是原子的)
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    
    t1.join();
    t2.join();
    
    std::cout << "Counter: " << counter << std::endl;
    return 0;
}
3. 读写锁 (Read-Write Lock)
cpp 复制代码
#include <iostream>
#include <thread>
#include <shared_mutex>
#include <vector>

std::shared_mutex rw_mutex;
std::vector<int> data = {1, 2, 3};

void reader(int id) {
    std::shared_lock<std::shared_mutex> lock(rw_mutex);  // 共享锁
    std::cout << "Reader " << id << " sees: ";
    for (int val : data) std::cout << val << " ";
    std::cout << std::endl;
}

void writer(int id) {
    std::unique_lock<std::shared_mutex> lock(rw_mutex);  // 独占锁
    data.push_back(data.back() + 1);
    std::cout << "Writer " << id << " added: " << data.back() << std::endl;
}
4. 条件变量 (Condition Variable)
cpp 复制代码
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>

std::mutex mtx;
std::condition_variable cv;
std::queue<int> data_queue;
bool finished = false;

void producer() {
    for (int i = 0; i < 10; ++i) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::lock_guard<std::mutex> lock(mtx);
        data_queue.push(i);
        std::cout << "Produced: " << i << std::endl;
        cv.notify_one();  // 通知消费者
    }
    
    std::lock_guard<std::mutex> lock(mtx);
    finished = true;
    cv.notify_all();
}

void consumer() {
    while (true) {
        std::unique_lock<std::mutex> lock(mtx);
        cv.wait(lock, []{ return !data_queue.empty() || finished; });
        
        while (!data_queue.empty()) {
            int data = data_queue.front();
            data_queue.pop();
            std::cout << "Consumed: " << data << std::endl;
        }
        
        if (finished) break;
    }
}
5. 线程局部存储 (Thread Local Storage)
cpp 复制代码
#include <iostream>
#include <thread>

thread_local int thread_specific_value = 0;  // 每个线程独立副本

void worker(int id) {
    thread_specific_value = id;
    for (int i = 0; i < 3; ++i) {
        thread_specific_value++;
        std::cout << "Thread " << id << ": " << thread_specific_value << std::endl;
    }
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    
    t1.join();
    t2.join();
    return 0;
}
6. 不可变对象 (Immutable Objects)
cpp 复制代码
#include <iostream>
#include <string>
#include <thread>

// 不可变类 - 所有成员都是const,创建后不可修改
class ImmutableData {
private:
    const int id_;
    const std::string name_;
    
public:
    ImmutableData(int id, std::string name) : id_(id), name_(std::move(name)) {}
    
    int getId() const { return id_; }
    const std::string& getName() const { return name_; }
};

void useData(const ImmutableData& data) {
    // 可以安全地在多个线程中访问,因为对象不可变
    std::cout << "ID: " << data.getId() << ", Name: " << data.getName() << std::endl;
}

三、线程安全设计原则

1. 最小化共享数据 :尽量设计无状态或线程局部的对象
2. 使用标准库设施 :优先使用std::atomic、智能指针等线程安全组件
3. 避免数据竞争 :确保对共享数据的访问有适当的同步
4. 避免死锁:

cpp 复制代码
// 坏例子:可能导致死锁
std::mutex mtx1, mtx2;

void thread1() {
    std::lock_guard<std::mutex> lock1(mtx1);
    std::lock_guard<std::mutex> lock2(mtx2);  // 如果thread2先锁mtx2,会死锁
}

// 好例子:使用std::lock同时锁定多个互斥量
void thread1_safe() {
    std::lock(mtx1, mtx2);  // 一次性锁定,避免死锁
    std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);
}

5.优先使用RAII:使用lock_guard、unique_lock等自动管理锁的生命周期

四、性能考虑

· 互斥锁有开销,过度使用会降低性能

· 读写锁在读多写少的场景中性能更好

· 原子操作通常比互斥锁更高效

· 无锁编程复杂但性能可能更好,适合高级场景

选择哪种线程安全机制取决于具体场景:数据竞争频率、读写比例、性能要求等。

相关推荐
Omics Pro12 分钟前
Cell封面|衰老生物学开源AI工具包
数据库·人工智能·算法·机器学习·自然语言处理
zhanghaha13141 小时前
Python进阶教程:35_PyQt (保姆级零基础教程)
开发语言·python·pyqt
释厄6231 小时前
多黑洞具现论·物理/生物/金融三域显化·虚拟宇宙黑洞·奇点=0 圆圈
开发语言·数据结构·人工智能·程序人生·安全威胁分析
UIU1142 小时前
递归算法与汉诺塔
c++·学习·算法·c#·递归
Fruit_Caller3 小时前
static
开发语言·c++
用户979984807188 小时前
200 行代码写一个能跑的 AI Agent:不依赖任何框架,只靠 tool-calling 原理
算法
threerocks8 小时前
【Muse实战】X 流量作战室搭建保姆级教程 - 拥有你自己的运营团队
算法
GreenTea8 小时前
深度拆解 ScienceBuddy:如何用“双层递归自进化”构建高可靠科研 Agent Harness
前端·后端·算法
花椒技术8 小时前
2.46 秒生成 5 秒视频:拆解 H3 Max 的模型、推理栈与硬件协同
算法·音视频开发·视频编码
vivo互联网技术8 小时前
ART:妆容迁移框架,重新定义高保真妆容迁移 | ECCV 2026
人工智能·算法·图像识别