C++并发编程(三):线程间共享数据

本文介绍了C++并发编程中如何避免恶性条件竞争,使用互斥量保护共享数据,以及如何处理死锁。讲解了互斥量的使用、层次锁的概念和unique_lock的优势,并给出了特殊情况下的数据保护策略,如初始化时的数据保护和读者-写者锁的应用。
摘要由CSDN通过智能技术生成

避免恶性条件竞争


双向链表具有前后两个链,当一个线程修改一个双向链表的前链还未来及修改后链时,另一个线程进行了数据访问,此时就会发生恶性条件竞争。多线程编程中,我们总会遇到多个线程对一个数据块进行修改,所以需要避免恶性条件竞争。避免恶性条件竞争有三个主要方法。

1、对数据块进行加锁等数据保护机制。同一时间,只有一个线程可对数据进行修改操作。

2、实现无锁化编程。对数据结构进行无锁化设计。

3、利用数据库中事务的思想,采用和事务一样的原理进行数据修改。

使用互斥量保护共享数据


互斥量是c++中最基本数据保护的方式。使用std::mutex来声明互斥量,使用lock()和unlock()函数进行加锁和解锁操作。因为使用lock()后,必须使用unlock()来解锁(包括异常情况),所以操作繁琐。我们可以利用RAII(获取资源即初始化)来完成这一操作。c++库提供了模板类std::lack_guard来完成在构造时加锁,析构时解锁的操作。两者都声明在<mutex>头文件。

#include <mutex>

std::mutex my_mutex;
void foo1(){
    my_mutex.lock();
    do_something();
    my_mutex.unlock();
}
void foo2(){
    std::lock_guard<std::mutex> my_guard(my_mutex);
    do_something();
}

foo1和foo2都加了互斥量,对数据进行了保护。

注意:要避免将保护数据的引用或者指针作为返回值传出或者参数传入,这将导致保护数据在保护域外被使用。

typedef void (*funType) (int&);
class foo{
private:
    int a = 0;
    mutex my_mutex;
public:
    void f(funType func){
        std::lock_guard<mutex> my_guard(my_mutex);
        func(a);
    }
    void show(){
        cout << a <<endl;
    }
};
int* unprotectd_data;
void func(int & a){
    unprotectd_data = &a;    //恶意或许a的地址
}

int main(){
    foo a;
    a.show();
    a.f(func);
    *unprotectd_data = 1;    //a的地址不受保护,在互斥量保护外修改
    a.show();
}

foo类的本意是只能在f函数内对变量a进行操作。但是当我们恶意地用指针来接收a变量的引用时,我们就可以在f的保护域外修改a。

互斥量范围大小与接口设计


假设我们有一个获取栈顶的函数pop

void pop(T & t){
    if(!stack.empty())
        t = stack.top();
        stack.pop();    
}   

假设stack的每一个函数都是线程安全的,但是我们的pop函数线程安全的吗?答案肯定是不安全。调用empty()之后,别的线程可能

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值