使用互斥量保护共享数据

C++中通过实例化 std::mutex 创建互斥量, 通过调用成员函数lock()进行上锁, unlock()进行解锁。 不过, 不推荐实践中直接去调用成员函数, 因为调用成员函数就意味着, 必须记住在每个函数出口都要去调用unlock(), 也包括异常的情况。

C++标准库为互斥量提供了一个RAII语法的模板类 std::lock_guard , 其会在构造的时候提供已锁的互斥量, 并在析构的时候进行解锁, 从而保证了一个已锁的互斥量总是会被正确的解锁。 

下面的程序清单中, 展示了如何在多线程程序中, 使用 std::mutex 构造的 std::lock_guard 实例, 对一个列表进行访问保护。 std::mutex std::lock_guard 都在 <mutex> 头文件中声明。 

#include <list>
#include <mutex>
#include <algorithm>
std::list<int> some_list; // 1
std::mutex some_mutex; // 2
void add_to_list(int new_value)
{
    std::lock_guard<std::mutex> guard(some_mutex); // 3
    some_list.push_back(new_value);
} 
bool list_contains(int value_to_find)
{
    std::lock_guard<std::mutex> guard(some_mutex); // 4
    return std::find(some_list.begin(),some_list.end(),value_to_find) != some_list.end(); 
}

上面代码中有一个全局变量, 这个全局变量被一个全局的互斥量保护add_to_list()list_contains()函数中使用 std::lock_guard<std::mutex> , 使得这两个函数中对数据的访问是互斥的: list_contains()不可能看到正在被add_to_list()修改的列表。 

虽然某些情况下, 使用全局变量没问题, 但在大多数情况下, 互斥量通常会与保护的数据放在同一个类中, 而不是定义成全局变量。 这是面向对象设计的准则: 将其放在一个类中, 就可让他们联系在一起, 也可对类的功能进行封装, 并进行数据保护。 在这种情况下, 函数add_to_listlist_contains可以作为这个类的成员函数。 互斥量和要保护的数据, 在类中都需要定义为private成员, 这会让访问数据的代码变的清晰, 并且容易看出在什么时候对互斥量上锁。 当所有成员函数都会在调用时对数据上锁, 结束时对数据解锁, 那么就保证了数据访问时不变量不被破坏。 

当其中一个成员函数返回的是保护数据的指针或引用时, 会破坏对数据的保护。 具有访问能力的指针或引用可以访问(并可能修改)被保护的数据, 而不会被互斥锁限制。 互斥量保护的数据需要对接口的设计相当谨慎, 要确保互斥量能锁住任何对保护数据的访问, 并且不留后门。 

posted @   小熊酱  阅读(13)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示