IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++多线程-条件竞争笔记 -> 正文阅读

[C++知识库]C++多线程-条件竞争笔记

1、先来看一段多线程代码:
std::shared_ptr<some_resource>  resource_prtr;
std::mutex resource_mutex;
void foo()
{
	std::unique_lock<std::mutex>	lk(resource_mutex);
	//此处所有线程序列化
	if(!resource_ptr)
	{
		resource_ptr.reset(new some_resource);//1,只有初始化过程需要保护
	}
	lk.unlock();
	resource_ptr->so_something();
}

由上一段代码可知,为了确定数据源已经初始化,每个线程都必须等待互斥量,可见没必要。
2、有人改进的双重检测锁模式:

void undefined_behaviour_with_double_checked_locing()
{	
	if(!resource_ptr)	//1
	{
		std::lock_guard<std::mutex> lk(resource_mutex);
		if(!!resource_ptr) //2
		{
			resource_ptr.reset(new some_resource);//3
		}
	}
	resource_ptr->so_something();//4
}

但是以上代码存在条件竞争风险:未被锁保护的读取操作1没有与其他线程里被锁保护的写入操作3进行同步,因此就会产生条件竞争。

3、c++引入了std::once_flag和std::call_once来处理以上这种情况。
比起锁住互斥量并显示检测指针,每个线程只需要使用std::call_once就可以,在std::call_once的结束时,就能安全指导指针是否被其他线程初始化了。
std::call_once比显示使用互斥量消耗的资源更少,特别是当初始化完成后。初始化通过调用函数完成

std::shared_ptr<some_resource> resource_ptr;
std::once_flag	resource_flag;//1

void init_resource()
{
	resource_ptr.reset(new some_resource);	
}
void foo()
{
	std::call_once(resource_flag, init_resource);//可以完整进行一次初始化
	resource_ptr->do_something();
}

4 、还有一种是使用std::call_once作为类成员的延迟初始化(线程安全)

class X
{
private:
	connection_info connection_details;
	connection_handle connection;
	std::once_flag connection_init_flag;
	
	void open_connection()
	{
		connection = connection_manager.open(connection_details);
	}
public:
	X(connection_info const& connection_details_):
	connection_details(connection_details_) {}
	void send_date(data_packet const& data)	//1
	{
		std::call_once(connection_init_flag, &X::open_connection, this);//2
		connection.send_data(data);
	}
	data_packet receive_data() //3
	{
		std::call_once(connection_init_flag, &X::open_connection, this);//4
		return connection.receive_data();		
	}
};	
}

以上例子中第一次调用send_data()或者receive_data的线程完成线程初始化过程。使用成员函数open_connection()去初始化数据,也需要将this制作穿进去。

5、还有一种初始化过程潜存着条件竞争:其中一个局部变量被声明为static类型,这种变量在声明时候进行初始化;对于多线程调用的初始化,意味着这里有条件竞争-抢着去定义这个变量。在C++11中,该问题已被解决:初始化以及定义,完全在一个线程中发生,并且没有其他线程可在初始化完成前对其进行处理。
更新操作少的情况,可以只在初始化保护数据。

class my_class;
my_class& get_my_class_instance() 
{
	static my_class instance;//线程安全的初始化过程。
	return instance;
}

6、互斥量std::mutex保护数据结构时,可能会削减并发读取数据的可能性。这里需要另一种不同的互斥量,这种互斥量被称为"读者-作者锁",因其允许两种不同的使用方式:一个writer线程独占访问和共享访问,让多个reader线程并发访问。
C++17提供了两种非常好的互斥量—std::shared_mutex和std::shared_time_mutex。C++14只提供了std::shared_timed_mutex,并且在C++11中并未提供任何互斥量类型。其中shared_time_mutex支持更多的操作方式,而shared_mutex拥有更高的性能优势。
可以使用std::shared_mutex进行数据同步:更新操作可以使用std::lock_guardstd::shared_mutex和std::unique_lockstd::shared_mutex上锁;读取操作可以使用std::shared_lockstd::shared_mutex来获取访问权。
唯一限制:当任一线程拥有一个共享锁时,这个线程就去尝试获取一个独占锁;当任一线程拥有一个独占锁,其他线程就无法获得共享锁或者独占锁,知道第一个线程放弃其拥有的锁。一下代码展示了一个简单的DNS缓存:

#include <map>
#include <string>
#include <mutex>
#include <shared_mutex>

class dns_entry;

class dns_cache
{
	std::map<std::string, dns_entry> entries;
	mutable std::shared_mutex entry_mutex;
public:
	dns_entry find_entry(std::string const& domain) const 
	{
		std::shared_lock<std::shared_mutex> lk(entry_mutex);//1
		std::map<std::string, dns_entry>::const_iterator const it = 
		entries.find(domain);
		return (it==entries.end())?dns_entry():it->second;
	}
	void update_or_add_entry(std::string const& domain, dns_entry const& dns_details)
	{
		std::lock_guard<std::shared_mutex> lk(entry_mutex);//2
		entries[domain]	= dns_details;
	}
};

其中update函数被调用时,独占锁将组织其他线程对数据结构进行修改,并且阻止线程调用find_entry()。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 13:40:22  更:2021-08-09 13:40:24 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/10 2:57:33-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码