什么是线程安全?

什么是线程安全?

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

一、非线程安全的例子:

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等自动管理锁的生命周期

四、性能考虑

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

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

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

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

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

相关推荐
Swift社区3 分钟前
LeetCode 469 凸多边形
算法·leetcode·职场和发展
chilavert3185 分钟前
技术演进中的开发沉思-298 计算机原理:算法的本质
算法·计算机原理
yesyesido11 分钟前
智能文件格式转换器:文本/Excel与CSV无缝互转的在线工具
开发语言·python·excel
Aaron158811 分钟前
全频段SDR干扰源模块设计
人工智能·嵌入式硬件·算法·fpga开发·硬件架构·信息与通信·基带工程
_200_13 分钟前
Lua 流程控制
开发语言·junit·lua
环黄金线HHJX.14 分钟前
拼音字母量子编程PQLAiQt架构”这一概念。结合上下文《QuantumTuan ⇆ QT:Qt》
开发语言·人工智能·qt·编辑器·量子计算
王夏奇14 分钟前
python在汽车电子行业中的应用1-基础知识概念
开发语言·python·汽车
He_Donglin15 分钟前
Python图书爬虫
开发语言·爬虫·python
求梦82016 分钟前
【力扣hot100题】缺失的第一个正数(12)
数据结构·算法·leetcode
星融元asterfusion24 分钟前
AsterNOS SONiC基于YANG模型的现代网络管理:从CLI到gNMI的演进
开发语言·sonic·yang