![](https://img-blog.csdnimg.cn/20210423103431488.png?x-oss-process=image/resize,m_fixed,h_224,w_224)
C++并发编程
文章平均质量分 69
C++ Concurrency In Action Practical
贾大君
这个作者很懒,什么都没留下…
展开
-
目录
目录书与作者 本书相关 前言 封面图片介绍 关于这本书 路线图 谁应该读这本书 如何使用这本书 本书概述 代码公约和下载 软件需求第1章 你好,C++的并发世界 1.1 何谓并发 1.1.1 计算机系统中的并发 1....翻译 2021-04-22 16:01:53 · 95 阅读 · 0 评论 -
第5章 C++内存模型和原子类型操作
第5章 C++内存模型和原子类型操作本章主要内容※ C++11内存模型详解 ※ 标准库提供的原子类型 使用各种原子类型 ※ 原子操作实现线程同步功能C++11标准中,有一个十分重要特性,常被程序员们所忽略。它不是一个新的语法特性,也不 是新的工具,它就是新的多线程(感知)内存模型。内存模型没有明确的定义基本部件应该如何 工作的话,之前介绍的那些工具就无法正常工作。那为什么大多数程序员都没有注意到它 呢?当你使用互斥量保护你的数据和条件变量,或者是“期望”上的信号事件时,对于互斥量为 什么.翻译 2021-04-22 16:04:05 · 141 阅读 · 0 评论 -
5.1 内存模型基础
5.1 内存模型基础这里从两方面来讲内存模型:一方面是基本结构,这个结构奠定了与内存相关的基础;另一 方面就是并发。基本结构对于并发也是很重要的,特别是当你阅读到底层原子操作的时候, 所以我将会从基本结构讲起。在C++中,它与所有的对象和内存位置有关。5.1.1 对象和内存位置在一个C++程序中的所有数据都是由对象(objects)构成。这不是说你可以创建一个int的衍生 类,或者是基本类型中存在有成员函数,或是像在Smalltalk和Ruby语言下讨论程序那样——“一切都是对象”。“对象.翻译 2021-04-22 16:11:20 · 113 阅读 · 0 评论 -
5.2 C++中的原子操作和原子类型
5.2 C++中的原子操作和原子类型原子操作是一类不可分割的操作,当这样操作在任意线程中进行一半的时候,你是不能查看的;它的状态要么是完成,要不就是未完成。如果从对象中读取一个值的操作是原子的,并且对对象的所有修改也都是原子的话,那么加载操作要不就会检索对象初始化的值,要不就将值存在某一次修改中。另一方面,非原子操作可能会被视为由一个线程完成一半的操作。如果这种是一个存储操 作,那么其他线程看到的,可能既不是存储前的值,也可能不是已存储的值。如果非原子操 作是一个加载操作,那么它可能会去检索对象的部翻译 2021-04-22 16:18:58 · 153 阅读 · 0 评论 -
5.2.1 标准原子类型
5.2.1 标准原子类型标准原子类型(atomic types)可以在头文件中找到。所有在这种类型上的操作都是原子的,虽然可以使用互斥量去达到原子操作的效果,但只有在这些类型上的操作是原子的(语言明确定义)。实际上,标准原子类型都很相似:它们(大多数)都有一个is_lock_free()成员函数,这个函数允许用户决定是否直接对一个给定类型使用原子指令(x.is_lock_free()返回true),或对编译器和运行库使用内部锁(x.is_lock_free()返回false)。只用std::atom翻译 2021-04-22 16:20:54 · 354 阅读 · 0 评论 -
5.2.2 std::atomic_flag的相关操作
5.2.2 std::atomic_flag的相关操作std::atomic_flag 是最简单的标准原子类型,它表示了一个布尔标志。这个类型的对象可以在两个状态间切换:设置和清除。它就是那么的简单,只作为一个构建块存在。我从未期待这个类型被使用,除非在十分特别的情况下。正因如此,它将作为讨论其他原子类型的起点,因为它会展示一些原子类型使用的通用策略。std::atomic_flag 类型的对象必须被ATOMIC_FLAG_INIT初始化。初始化标志位是“清除”状态。这里没得选择;这个标志总是初始化翻译 2021-04-22 16:26:04 · 274 阅读 · 0 评论 -
5.2.3 std::atomic的相关操作
5.2.3 std::atomic的相关操作最基本的原子整型类型就是 std::atomic<bool> 。如你所料,它有着比 std::atomic_flag更加齐全的布尔标志特性。虽然它依旧不能拷贝构造和拷贝赋值,但是你可以使用一个非原子的bool类型构造它,所以它可以被初始化为true或false,并且你也可以从一个非原子bool变量赋值给 std::atomic<bool> 的实例:std::atomic<bool> b(true);b = false翻译 2021-04-22 16:28:03 · 5119 阅读 · 0 评论 -
5.2.4 std::atomic:指针运算
5.2.4 std::atomic:指针运算原子指针类型,可以使用内置类型或自定义类型T,通过特化 std::atomic<T*> 进行定义,就 如同使用bool类型定义 std::atomic<bool> 类型一样。虽然接口几乎一致,但是它的操作是对于相关的类型的指针,而非bool值本身。就像 std::atomic<bool> ,虽然它既不能拷贝构造,也不能拷贝赋值,但是他可以通过合适的类型指针进行构造和赋值。如同成员函数 is_lock_free()一样,std翻译 2021-04-22 16:56:00 · 1801 阅读 · 0 评论 -
5.2.5 标准的原子整型的相关操作
5.2.5 标准的原子整型的相关操作如同普通的操作集合一样(load(), store(), exchange(), compare_exchange_weak(), 和 compare_exchange_strong()),在 std::atomic<int> 和 std::atomic<unsigned long long> 也 是有一套完整的操作可以供使用:fetch_add(), fetch_sub(), fetch_and(), fetch_or(), fetch_xo翻译 2021-04-22 16:57:18 · 139 阅读 · 0 评论 -
5.2.6 std::atomic<>主要类的模板
5.2.6 std::atomic<>主要类的模板主模板的存在,在除了标准原子类型之外,允许用户使用自定义类型创建一个原子变量。不 是任何自定义类型都可以使用std::atomic<> 的:需要满足一定的标准才行。为了使用 std::atomic<UDT> (UDT是用户定义类型),这个类型必须有拷贝赋值运算符。这就意味着这个类型不能有任何虚函数或虚基类,以及必须使用编译器创建的拷贝赋值操作。不仅仅是这 些,自定义类型中所有的基类和非静态数据成员也都需要支持拷贝赋值操翻译 2021-04-22 16:58:21 · 868 阅读 · 0 评论 -
5.2.7 原子操作的释放函数
5.2.7 原子操作的释放函数直到现在,我都还没有去描述成员函数对原子类型操作的形式。但是,在不同的原子类型中 也有等价的非成员函数存在。大多数非成员函数的命名与对应成员函数有关,但是需 要“atomic_”作为前缀(比如,std::atomic_load() )。这些函数都会被不同的原子类型所重载。 在指定一个内存序列标签时,他们会分成两种:一种没有标签,另一种将“_explicit”作为后缀,并且需要一个额外的参数,或将内存顺序作为标签,亦或只有标签(例如,std::atomic_store(&am翻译 2021-04-22 16:59:29 · 977 阅读 · 0 评论 -
5.3 同步操作和强制排序
5.3 同步操作和强制排序假设你有两个线程,一个向数据结构中填充数据,另一个读取数据结构中的数据。为了避免恶性条件竞争,第一个线程设置一个标志,用来表明数据已经准备就绪,并且第二个线程在这个标志设置前不能读取数据。下面的程序清单就是这样的情况。清单5.2 不同线程对数据的读写#include <vector>#include <atomic>#include <iostream>#include <chrono>#include <翻译 2021-04-22 17:02:32 · 188 阅读 · 0 评论 -
5.3.1 同步发生
5.3.1 同步发生“同步发生”关系是指:只能在原子类型之间进行的操作。例如对一个数据结构进行操作(对互斥量上锁),如果数据结构包含有原子类型,并且操作内部执行了一定的原子操作,那么这些操作就是同步发生关系。从根本上说,这种关系只能来源于对原子类型的操作。“同步发生”的基本想法是:在变量x进行适当标记的原子写操作W,同步与对x进行适当标记的 原子读操作,读取的是W操作写入的内容;或是在W之后,同一线程上的原子写操作对x写入 的值;亦或是任意线程对x的一系列原子读-改-写操作(例如,fetch_add翻译 2021-04-22 17:06:13 · 171 阅读 · 0 评论 -
5.3.2 先行发生
5.3.2 先行发生“先行发生”关系是一个程序中,基本构建块的操作顺序: 它指定了某个操作去影响另一个操作。对于单线程来说,就简单了:当一个操作排在另一个之后,那么这个操作就是先行执行 的。这意味着,如果源码中操作A发生在操作B之前,那么A就先行与B发生。你可以回看清单 5.2:对data的写入③先于对data_ready④的写入。如果操作在同时发生,因为操作间无序执行,通常情况下,它们就没有先行关系了。这就是另一种排序未被指定的情况。下面的程序会输出“1,2”或“2,1”,因为两个get_num()的翻译 2021-04-22 17:12:45 · 148 阅读 · 0 评论 -
与同步传递相关的获取-释放序列
与同步传递相关的获取-释放序列为了考虑传递顺序,你至少需要三个线程。第一个线程用来修改共享变量,并且对其中一个 做“存储-释放”处理。然后第二个线程使用“加载-获取”读取由“存储-释放”操作过的变量,并且 再对第二个变量进行“存储-释放”操作。最后,由第三个线程通过“加载-获取”读取第二个共享变量。提供“加载-获取”操作,来读取被“存储-释放”操作写入的值,是为了保证同步关系,这里即便是中间线程没有对共享变量做任何操作,第三个线程也可以读取被第一个线程操作过 的变量。下面的代码可以用来描述这样的场景。翻译 2021-04-21 17:59:41 · 204 阅读 · 0 评论 -
5.3.6 原子操作对非原子的操作排序
5.3.6 原子操作对非原子的操作排序当你使用一个普通的非原子bool类型来替换清单5.12中的x(就如同你下面看到的代码),行为 和替换前完全一样。清单5.13 使用非原子操作执行序列#include <atomic>#include <thread>#include <assert.h>bool x = false; // x现在是一个非原子变量std::atomic<bool> y;std::atomic&...翻译 2021-04-22 15:01:27 · 126 阅读 · 0 评论 -
5.4 总结
5.4 总结在本章中,已经对C++11内存模型的底层只是进行详尽的了解,并且了解了原子操作能在线程间提供基本的同步。这里包含基本的原子类型,由std::atomic<>类模板特化后提供;接口,以及对于这些类型的操作,还要有对内存序列选项的各种复杂细节,都由原始 std::atomic<> 类模板提供。我们也了解了栅栏,了解其如何让执行序列中,对原子类型的操作同步成对。最后,我们回顾了本章开始的一些例子,了解了原子操作可以在不同线程上的非原子操作间,进行有序执 行。在下一.翻译 2021-04-22 15:34:09 · 106 阅读 · 0 评论 -
第6章 基于锁的并发数据结构设计
第6章 基于锁的并发数据结构设计本章主要内容※并发数据结构设计的意义 指导如何设计※实现为并发设计的数据结构在上一章中,我们对底层原子操作和内存模型有了详尽的了解。在本章中,我们将先将底层 的东西放在一边(将会在第7章再次提及),来对数据结构做一些讨论。数据结构的选择,对于程序来说,是其解决方案的重要组成部分,当然,并行程序也不例 外。如果一种数据结构可以被多个线程所访问,其要不就是绝对不变的(其值不会发生变化, 并且不需同步),要不程序就要对数据结构进行正确的设计,以确保其..翻译 2021-04-22 15:35:42 · 107 阅读 · 0 评论