一 线程间数据共享的问题

1. 线程间数据共享带来了很多好处,但同时也有很多问题。而问题的根源在于对数据的修改. 在并行编程中,一个常见的场景就时:条件竞争( race condition);

2. 避免条件竞争的方法: a. 保护机制  b. 将change变为不可分割的changes (lock-free programming) c. 将数据更新作为一个事务( transaction)

SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。

二 利用互斥保护共享数据

1. 通过std::mutex创建一个互斥对象,通过调用lock()函数实现加锁,unlock()实现放锁。在实际使用中不建议直接调用这两个函数,如果直接调用这两个函数需要确保所有路径都必须确保unlock,包括有异常抛出的情况。c++函数库提供了std::lock_guard,为互斥对象实现RAII

#include <list>
#include <mutex>
#include <algorithm>
std::list<int> some_list;
std::mutex some_mutex;

void add_to_list(int new_value)
{
    std::lock_guard<std::mutex> guard(some_mutex);
    some_list.push_back(new_value);
}

bool list_contains(int value_to_find)
{
std::lock_guard<std::mutex> guard(some_mutex); return std::find(some_list.begin(),some_list.end(),value_to_find) != some_list.end(); }

 

2. 需要注意的是,如果一个函数返回保护数据的引用或指针或做为out参数,被保护数据则会脱离锁的保护,所以对于互斥锁保护的数据,在设计接口时一定要小心。
   同时,将被保护数据的引用或者指针作为参数传递给其他函数也时十分危险的,这些函数可能会存储被保护数据的引用或者指针,让其脱离锁的保护。如下面这个例子:

class some_data
{
int a;
    std::string b;
public:
    void do_something();
};
class data_wrapper
{
private:
    some_data data;
    std::mutex m;
public:
    template<typename Function>
    void process_data(Function func)
    {
        std::lock_guard<std::mutex> l(m);
        func(data);
    }
};

some_data* unprotected; // 这个数据不在受保护。
void malicious_function(some_data& protected_data)
{
    unprotected=&protected_data;
}
data_wrapper x; 
void foo() {
    x.process_data(malicious_function);
    unprotected->do_something();
}

所以一个常用的原则是: 不要将被保护数据的引用或指针传递到锁的范围外。即不要将其做为返回值, OUT对象,或者将其传递到用户定义的函数中。

 

 

 

 

 

 

  

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄