当前位置: 首页 > news >正文

C++多线程【数据共享】之互斥锁

一、互斥锁的使用

访问一个数据结构前,先锁住与数据相关的互斥;访问结束后,再解锁互斥。
c++标准库提供了类模板std::lock_guard<>,针对互斥类融合实现了RAII手法:在构造时互斥加锁,在析构时解锁,从而保证互斥总被正确解锁。

#include <algorithm>
#include <list>
#include <mutex>
#include <thread>std::list<int> some_list;  // shared data
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();
}

需要注意的是,一旦调用者返回指针或引用,指向受保护的共享数据,就会危及共享数据安全。
所以,有个原则就是不得向锁所在的作用域之外传递指针和引用,指向受保护的数据
错误示范如下:

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;  // Unsafe exposure of protected data
}
data_wrapper x;
void foo() {// 传入恶意函数x.process_data(malicious_function);// 以无保护方式访问本应受保护的共享数据unprotected->do_something();  // Unsafe use of protected data
}

线程安全的栈容器类

#include <exception>
#include <memory>
#include <mutex>
#include <stack>struct empty_stack : std::exception {const char* what() const throw();
};template <typename T>
class threadsafe_stack {private:std::stack<T> data;mutable std::mutex m;public:threadsafe_stack() {}threadsafe_stack(const threadsafe_stack& other) {std::lock_guard<std::mutex> lock(other.m);data = other.data;}threadsafe_stack& operator=(const threadsafe_stack&) = delete;void push(T new_value) {std::lock_guard<std::mutex> lock(m);data.push(std::move(new_value));}std::shared_ptr<T> pop() {std::lock_guard<std::mutex> lock(m);if (data.empty()) throw empty_stack();value = data.top();data.pop();}bool empty() const {std::lock_guard<std::mutex> lock(m);return data.empty();}
};

二、死锁

有两个线程,都需要同时锁住两个互斥,才可以进行某项操作,单他们分别都只锁住了一个互斥,都等着再给另外一个互斥加锁。

防范死锁的建议通常是,始终按相同顺序对两个互斥加锁。若我们总是先锁互斥A再锁互斥B,则永远不会发生死锁。

如果对两个数据进行多线程互换操作时,可如下对数据进行加锁操作:

class some_big_object;
void swap(some_big_object& lhs, some_big_object& rhs);
class X {private:some_big_object some_detail;std::mutex m;public:X(some_big_object const& sd) : some_detail(sd) {}friend void swap(X& lhs, X& rhs) {if (&lhs == &rhs) return;std::lock(lhs.m, rhs.m);std::lock_guard<std::mutex> lock_a(lhs.m, std::adopt_lock);std::lock_guard<std::mutex> lock_b(rhs.m, std::adopt_lock);swap(lhs.some_detail, rhs.some_detail);}
};// c++17版本
void swap(X& lhs, X& rhs) {if (&lhs == &rhs) return;// c++17新增了另外一个特性:类模板参数推导std::scoped_lock lock(lhs.m, rhs.m);swap(lhs.some_detail, rhs.some_detail);
}// std::scoped_lock<std::mutex, std::mutex> guard(lhs.m, rhs.m);

防范死锁的补充原则:
假定有两个线程,各自关联了std::thread实例,若它们同时在对方的std::thread示例上调用join(),就能制造出死锁现象却不涉及锁操作。
防范死锁的准则最终可归纳成一个思想:只要另一线程有可能正在等待当前线程,那么当前线程千万不能反过来等待它。
1.避免嵌套锁;
2.一旦持锁,就避免调用由用户提供的程序接口;
3.依从固定顺序获取锁
4.按层级加锁
锁的层级划分就是按照特定方式规定加锁次序,在运行期据此查验加锁操作是否遵从预设规则。
如下是多线程锁使用的一个示范用例

#include <mutex>
class hierarchical_mutex {std::mutex internal_mutex;unsigned long const hierarchy_value;unsigned long previous_hierarchy_value;static thread_local unsigned long this_thread_hierarchy_value;void check_for_hierarchy_violation() {if (this_thread_hierarchy_value <= hierarchy_value) {throw std::logic_error("mutex hierarchy violated");}}void update_hierarchy_value() {previous_hierarchy_value = this_thread_hierarchy_value;this_thread_hierarchy_value = hierarchy_value;}public:explicit hierarchical_mutex(unsigned long value): hierarchy_value(value), previous_hierarchy_value(0) {}void lock() {check_for_hierarchy_violation();internal_mutex.lock();update_hierarchy_value();}void unlock() {if (this_thread_hierarchy_value != hierarchy_value) {throw std::logic_error("mutex hierarchy violated on unlock");}this_thread_hierarchy_value = previous_hierarchy_value;internal_mutex.unlock();}bool try_lock() {check_for_hierarchy_violation();if (!internal_mutex.try_lock()) return false;update_hierarchy_value();return true;}
};//实际使用的时候
hierarchical_mutex high_level_mutex(10000);
hierarchical_mutex low_level_mutex(5000);
hierarchical_mutex other_mutex(6000);int do_low_level_stuff();int low_level_func() {std::lock_guard<hierarchical_mutex> lk(low_level_mutex);return do_low_level_stuff();
}void high_level_stuff(int some_param);
void high_level_func() {std::lock_guard<hierarchical_mutex> lk(high_level_mutex);high_level_stuff(low_level_func());
}void thread_a() { high_level_func(); }void do_other_stuff();
void other_stuff() {high_level_func();do_other_stuff();
}void thread_b() {// 拥有低层级的锁不能获取层级高的锁,会抛出异常std::lock_guard<hierarchical_mutex> lk(other_mutex);other_stuff();
}

5.将准则推广到锁操作之外

3.运用std::unique_lock<>灵活加锁
std::unique_lock占用更多的空间,也比std::lock_guard略慢。但std::unique_lock对象可以不占有关联的互斥,具备这份灵活性需要付出代价:需要存储并更新互斥信息。


class some_big_object;
void swap(some_big_object& lhs, some_big_object& rhs);
class X {private:some_big_object some_detail;std::mutex m;public:X(some_big_object const& sd) : some_detail(sd) {}friend void swap(X& lhs, X& rhs) {if (&lhs == &rhs) return;std::lock(lhs.m, rhs.m);std::unique_lock<std::mutex> lock_a(lhs.m, std::defer_lock);std::unique_lock<std::mutex> lock_b(rhs.m, std::defer_lock);// 确保两个锁都被获取到,加锁std::lock(lock_a, lock_b);swap(lhs.some_detail, rhs.some_detail);}
};
``**4.按适合的颗粒度加锁**```cpp
void get_and_process_data() {std::unique_lock<std::mutex> my_lock(the_mutex);some_class data_to_process = get_next_data_chunk();my_lock.unlock();result_type result = process(data_to_process);my_lock.lock();store_result(result);
}
http://www.dtcms.com/a/618286.html

相关文章:

  • Java漏洞集合工具
  • JavaScript 正则表达式详解
  • 【CS创世SD NAND征文】高可靠性数控设备:技术方案与行业展望
  • 深入理解Go语言Slice的append操作:从内存分配到扩容机制
  • Linux---文件控制<fcntl.h> (file control, fcntl)
  • 网站放到服务器珠海市 网站建设
  • 农林科技公司网站模板seo研究中心官网
  • 东莞响应式网站哪家好架设网站开发环境
  • 类似淘宝网站建设有哪些模板wordpress文章图片全屏浏览
  • 技术演进中的开发沉思-194 JavaScript: Prototype 框架
  • Windows MongoDB 安装与配置指南
  • Kafka客户端整合
  • 购物网站建设方案手机建立网站的软件
  • 力扣hot100----1day
  • 二叉树的前序遍历解题思路
  • python手写数字识别计分系统+CNN模型+YOLOv5模型 深度学习 计算机毕业设计(建议收藏)✅
  • 网站服务器租赁价格上海低价网站建设
  • 基于Python房价预测系统 数据分析 Flask框架 爬虫 随机森林回归预测模型、链家二手房 可视化大屏 大数据毕业设计(附源码)✅
  • linux服务-tomcat原理与安装
  • Kotlinx.serialization 对多态对象(sealed class )支持更好用
  • ArkTS接口与泛型在HarmonyOS应用开发中的深度应用
  • 4.4 跨越文本边界!多模态Agent开发实战,视觉+语言融合的新可能
  • 【数据结构】从零开始认识B树 --- 高效外查找的数据结构
  • 东莞seo网站排名优化建立外贸网站多少钱
  • 有没有什么做地堆的网站wordpress 文章摘要字数
  • stateflow和shareflow的区别
  • Qt QLibrary程序在运行时加载外部库
  • 电线电缆做销售哪个网站好海南哪家公司做网站做的好
  • 做it题的网站知名网站欣赏
  • 番禺做网站哪家强网站定位方案