求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
C++并发编程(中文版)
前言
第1章 你好,C++的并发世界!
1.1 何谓并发
1.2 为什么使用并发?
1.3 C++中的并发和多线程
1.4 开始入门
1.5 本章总结
第2章 线程管理
2.1 线程管理的基础
2.2 向线程函数传递参数
2.3 转移线程所有权
2.4 运行时决定线程数量
2.5 识别线程
2.6 本章总结
第3章 线程间共享数据
3.1 共享数据带来的问题
3.2 使用互斥量保护共享数据
3.3 保护共享数据的替代设施
3.4 本章总结
第4章 同步并发操作
4.1 等待一个事件或其他条件
4.2 使用期望等待一次性事件
4.3 限定等待时间
4.4 使用同步操作简化代码
4.5 本章总结
第5章 C++内存模型和原子类型操作
5.1 内存模型基础
5.2 C++中的原子操作和原子类型
5.3 同步操作和强制排序
5.4 本章总结
第6章 基于锁的并发数据结构设计
6.1 为并发设计的意义何在?
6.2 基于锁的并发数据结构
6.3 基于锁设计更加复杂的数据结构
6.4 本章总结
第7章 无锁并发数据结构设计
7.1 定义和意义
7.2 无锁数据结构的例子
7.3 对于设计无锁数据结构的指导建议
7.4 本章总结
第8章 并发代码设计
8.1 线程间划分工作的技术
8.2 影响并发代码性能的因素
8.3 为多线程性能设计数据结构
8.4 设计并发代码的注意事项
8.5 在实践中设计并发代码
 

 
目录
为并发设计的意义何在?
作者:Anthony Williams  译者:陈晓伟
18 次浏览
1次  

6.1 为并发设计的意义何在?

设计并发数据结构,意味着多个线程可以并发的访问这个数据结构,线程可对这个数据结构做相同或不同的操作,并且每一个线程都能在自己的自治域中看到该数据结构。且在多线程环境下,无数据丢失和损毁,所有的数据需要维持原样,且无条件竞争。这样的数据结构,称之为“线程安全”的数据结构。通常情况下,当多个线程对数据结构进行同一并发操作是安全的,但不同操作则需要单线程独立访问数据结构。或相反,当线程执行不同的操作时,对同一数据结构的并发操作是安全的,而多线程执行同样的操作,则会出现问题。

实际的设计意义并不止上面提到的那样:这就意味着,要为线程提供并发访问数据结构的机会。本质上,是使用互斥量提供互斥特性:在互斥量的保护下,同一时间内只有一个线程可以获取互斥锁。互斥量为了保护数据,显式的阻止了线程对数据结构的并发访问。

这被称为序列化(serialzation):线程轮流访问被保护的数据。这其实是对数据进行串行的访问,而非并发。因此,你需要对数据结构的设计进行仔细斟酌,确保其能真正并发访问。虽然,一些数据结构有着比其他数据结构多的并发访问范围,但是在所有情况下的思路都是一样的:减少保护区域,减少序列化操作,就能提升并发访问的潜力。

在我们进行数据结构的设计之前,让我们快速的浏览一下,在并发设计中的指导建议。

6.1.1 数据结构并发设计的指导与建议(指南)

如之前提到的,当设计并发数据结构时,有两方面需要考量:一是确保访问是安全的,二是能真正的并发访问。在第3章的时候,已经对如何保证数据结构是线程安全的做过简单的描述:

  • 确保无线程能够看到,数据结构的“不变量”破坏时的状态。

  • 小心那些会引起条件竞争的接口,提供完整操作的函数,而非操作步骤。

  • 注意数据结构的行为是否会产生异常,从而确保“不变量”的状态稳定。

  • 将死锁的概率降到最低。使用数据结构时,需要限制锁的范围,且避免嵌套锁的存在。

在你思考设计细节前,你还需要考虑这个数据结构对于使用者来说有什么限制;当一个线程通过一个特殊的函数对数据结构进行访问时,那么还有哪些函数能被其他的线程安全调用呢?

这是一个很重要的问题,普通的构造函数和析构函数需要独立访问数据结构,所以用户在使用的时候,就不能在构造函数完成前,或析构函数完成后对数据结构进行访问。当数据结构支持赋值操作,swap(),或拷贝构造时,作为数据结构的设计者,即使数据结构中有大量的函数被线程所操纵时,你也需要保证这些操作在并发环境下是安全的(或确保这些操作能够独立访问),以保证并发访问时不会出现错误。

第二个方面是,确保真正的并发访问。这里没法提供更多的指导意见;不过,作为一个数据结构的设计者,在设计数据结构时,自行考虑以下问题:

  • 锁的范围中的操作,是否允许在所外执行?

  • 数据结构中不同的区域是否能被不同的互斥量所保护?

  • 所有操作都需要同级互斥量保护吗?

  • 能否对数据结构进行简单的修改,以增加并发访问的概率,且不影响操作语义?

这些问题都源于一个指导思想:如何让序列化访问最小化,让真实并发最大化?允许线程并发读取的数据结构并不少见,而对数据结构的修改,必须是单线程独立访问。这种结构,类似于boost::shared_mutex。同样的,这种数据结构也很常见——支持在多线程执行不同的操作时,并序列化执行相同的操作的线程(你很快就能看到)。

最简单的线程安全结构,通常使用的是互斥量和锁,对数据进行保护。虽然,这么做还是有问题(如同在第3中提到的那样),不过这样相对简单,且保证只有一个线程在同一时间对数据结构进行一次访问。为了让你轻松的设计线程安全的数据结构,接下来了解一下基于锁的数据结构,以及第7章将提到的无锁并发数据结构的设计。


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



18 次浏览
1次