Notes
HBase
基于 Hadoop 分布式文件系统使用 SSTables 和 LSM-Tree 实现随机访问的 OLTP 数据库。
大规模并行处理
MPP 数据库 Gamma 数据库机器 Teradata Tandem NonStop SQL
Clock Synchronization with Chris Perl
tags: 分布式,一致性,Clock Synchronization,Multicast source: https://signalsandthreads.com/clock-synchronization/ Electronic Oscillator: Computer itself to Dervie its Notion of Time Computer’s clock are based on a 1 MHz electronic oscillator circuit, that is oscillating at some frequency, and driving an interrupt. So the operating system can use it to derive its notion of time. It helps computer to keep the time correct. But a bad oscillator could be influenced by the heat of CPU, like compiling Linux kernel, etc. A really high-quality oscillator would be really expensive. So most computers come with fairly bad oscillators. ...
Hive
tags: Bigdata bucketed map join
Hadoop
tags: Bigdata Hadoop Distributed File System MapReduce MapReduce shuffle 按照 reducer 分区,排序和将数据分区从 mapper 复制到 reducer。(令人困惑的术语,并不完全与洗牌一样,在 MapReduce 中其实没有随机性)。 MapReduce 的分布式执行 Hadoop MapReduce 并行化基于数据分区实现: 输入:通常是 HDFS 中的一个目录。 分区:每个文件或文件块都被视为一个单独的分区。 处理:每个分区由单独的 map 任务来处理。 每个 mapper 都会尽量实现计算靠近数据。 代码复制:JAR 文件。 Reduce 任务的计算也被分隔成块,可以不必与 mapper 任务数量相同,MapReduce 框架使用关键字的哈希值来确保具有相同关键字的键值对都在相同的 reduce 任务中处理。 键值对必须进行排序,排序是分阶段进行的: 每个 map 任务都基于关键字哈希值,按照 reducer 对输出进行分块。 每个分区都被写入 mapper 程序所在的本地磁盘上的已排序文件,参见 SSTables 和 LSM-Tree。 reducer 与每个 mapper 相连接:MapReduce 调度器会在 mapper 写入经过排序的输出文件后,通知 reducer 开始从 mapper 中获取输出文件,框架进行 MapReduce shuffle。 reducer 任务从 mapper 中获取文件并将它们合并在一起,同时保持数据的排序。不同 mapper 使用相同的关键字生成记录,会在合并后的 reducer 输入中位于相邻的位置。 reducer 可以使用任意逻辑来处理这些记录,并且生成任意数量的输出记录。记录被写入分布式文件系统中的文件。 MapReduce 工作流调度器 Oozie Azkaban Luigi Airflow Pinball 对比分布式数据库 MapReduce 中的并行处理和并行 join 算法已经在十多年前所谓的大规模并行处理(MPP)数据库中实现了。 ...
Tokio
tags: Rust
分布式文件系统
tags: 分布式
网络连接存储
Hadoop Distributed File System
tags: Bigdata,分布式文件系统 与网络连接存储(NAS)和 存储区域网络(SAN)架构相比,HDFS 基于无共享原则,无需定制硬件和特殊网络基础设施(光纤)。 HDFS 创建了一个庞大的文件系统,来充分利用每个守护进程机器上的磁盘资源。 HDFS 包含一个在每台机器上运行的守护进程,并会开放一个网络服务以允许其他节点访问存储在该机器上的文件。 名为 NameNode 的中央服务器会跟踪哪个文件块存储在哪个服务器上。 考虑容错,文件快块复制到多台机器上,或者像 Reed-Solomon 代码中这样的纠删码方案(类似 RAID,但无需特殊硬件)。 提供很好的扩展性,配合商业硬件和开源软件,可以运行在上万台机器,容量达几百 PB。 计算靠近数据 只要有足够的空闲内存和 CPU 资源,MapReduce 调度器会尝试在输入文件的副本的某台机器上运行 mapper 任务。
Emacs Easter egg
tags: Emacs M-x life RET 康威生命游戏(Conway’s Game of Life)
数据库
tags: 技术 数据库作为一个长期发展的技术,但是在中国相对处于一个起步阶段,相关人才比较少。近年能够看得到的技术: TiDB 分布式关系型数据库 TDengine 面向 IoT 的 OLAP 数据库 相关创业公司: 神策 https://zhuanlan.zhihu.com/p/396433354
批处理系统
MapReduce MapReduce 与分布式文件系统 MapReduce 就像分布在上千台机器上的 Unix 工具。 MapReduce 作业通常不会修改输入,除了输出外没有任何副作用。 MapReduce 作业在分布式文件系统上读写。(Unix 工具 stdin、stdout),如 HDFS(Hadoop Distributed File System)等(GlusterFS、QFS、Amazon S3、Azure Blob 和 OpenStack Swift)。 MapReduce 作业执行 MapReduce 是一个编程框架,可以使用它编写代码处理 HDFS 等分布式文件系统中的大型数据集。 要创建 MapReduce 作业需要实现两个回调函数: mapper 和 reducer (另请参阅 MapReduce 查询): Mapper: 每个输入记录都会调用一次,从输入记录提取任意数量的关键字和值(可以为空),不保留任何状态,可以独立处理。 Reducer: MapReduce 框架使用 Mapper 生成的键值对,收集同一个关键字的所有值,并使用迭代器调用 reducer 以使用该值的集合。 Reducer 可以生成输出记录。 MapReduce 分布式执行 参见 Hadoop 的 MapReduce 的分布式执行。 MapReduce 工作流 将 MapReduce 作业链接到工作流是非常普遍的,作业的输出作为下一个作业的输入。通过目录名隐式的完成: 第一个作业必须配置将其输出写入 HDFS 中指定目录; 第二个作业必须配置读取相同的目录名作为输入。 目前已经开发了处理依赖管理的 MapReduce 工作流调度器。 Reduce 端的 join 与分组 批处理的背景下讨论 join,主要解决数据集内存在关联的所有事件。 假设 join 两张表:用户和活动事件。 ...
LeetCode: 37. Sudoku Solver
tags: LeetCode
LeetCode: 36. Valid Sudoku
tags: LeetCode https://leetcode.com/problems/valid-sudoku/ <- high -- low -> +------------------- wow(row(i):0,col(j):0) 0 -> [ 0010, 0010 ] | 1 -> [ 0000, 0000 ] | 2 -> [ 0000, 0000 ] | | +--------------- wow(row(i):0,col(j):1) 0 -> [ 0010 | 1 = 0011, 0010 ] | | 1 -> [ 0000, 0000 | 1 = 0001 ] | | 2 -> [ 0000, 0000 ] | | | | +----------- wow(row(i):0,col(j):2) 0 -> [ 0011 | 3 = 0100, 0010 ] | | | 1 -> [ 0000, 0001 ] | | | 2 -> [ 0000, 0000 | 3 = 0100 ] +---+---+---+ | 2 | 1 | 3 | +---+---+---+ ----| 3 | 2 | 1 | | +---+---+---+ | | 1 | 3 | 2 | | +---+---+---+ |- wow(row(i):1,col(j):0) 0 -> [0010 | 3 = 0110, 0010] +---------------------+ 1 -> [0000, 0001 | 3 = 0101] 2 -> [0000, 0100] class Solution { public: bool isValidSudoku(vector<vector<char>>& board) { vector<int> wow(9,0); int mux1; int mux2; int mux3; int box_index; for(int i=0;i<9;i++){ for(int j=0;j<9;j++){ if(board[i][j] == '.'){ continue; } mux1 = 0x01 << (board[i][j] - '1'); mux2 = 0x01 << 9 << (board[i][j] - '1'); mux3 = 0x01 << 9 << 9 << (board[i][j] - '1'); box_index = (i/3) * 3 + j/3; if((wow[i]&mux1) != mux1 && (wow[j]&mux2) != mux2 && (wow[box_index]&mux3) != mux3){ wow[i] = wow[i]|mux1; wow[j] = wow[j]|mux2; wow[box_index] = wow[box_index]|mux3; } else{ return false; } } } return true; } };
区块链
tags: 分布式共识,技术概念
LeetCode: 40. Combination Sum II
tags: LeetCode source: https://leetcode.com/problems/combination-sum-ii/ LeetCode: 39. Combination Sum 的进阶。元素不在唯一且每一个元素只能出现一次。对结果进行排序然后通过 set 对结果进行去重: class Solution { public: vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { sort(candidates.begin(), candidates.end()); backtracking(candidates, 0, 0, target); vector<vector<int>> r; for (auto t : res) { r.push_back(t); } return r; } private: set<vector<int>> res; vector<int> track; map<int, bool> visited; void backtracking(vector<int>& condidates, int start, int n, int target) { if (n == target) { res.insert(track); return; } if (n > target) { return; } int c = 0; int sz = condidates.size(); for (int i = start; i < sz; i++) { c = condidates[i]; track.push_back(c); backtracking(condidates, i + 1, n + c, target); track.pop_back(); } } }; 以下测试用例无法通过: ...
LeetCode: 39. Combination Sum
tags: LeetCode source: https://leetcode.com/problems/combination-sum/ class Solution { public: vector<vector<int>> combinationSum(vector<int>& candidates, int target) { backtracking(candidates, 0, target); return res; } private: vector<vector<int>> res; vector<int> track; void backtracking(vector<int> & candidates, int n, int target) { if (n == target) { res.push_back(track); return; } // this is new if (n > target) { return; } for (auto c : candidates) { track.push_back(c); backtracking(candidates, n + c, target); track.pop_back(); } } }; 问题:会有不同顺序但是元素相同的数组,如何快速高效的进行过滤? ...
LeetCode: 52. N-Queens II
tags: LeetCode,backtracking source: https://leetcode.com/problems/n-queens-ii/ 参见:LeetCode: 51. N-Queens
回溯算法
tags: Algorithm,Brute Force Approach
工业云
tags: 技术概念 创业公司有:积梦智能。
云原生
tags: 技术概念
云计算
tags: 技术概念
技术概念
tags: 技术 目前互联网领域里比较热门的概念和方向。
边缘计算
tags: 技术概念
LeetCode: 51. N-Queens
tags: LeetCode,backtracking source: https://leetcode.com/problems/n-queens/ 一旦一个 Queue 被放置,那么横轴、纵轴、对角线都不再允许放置。我们按行进行遍历,所以我们需要跟踪以下位置是否已经放置 Queue: 纵轴(Column):cols 主对角线(Positive Diagonal):posDiag 次对角线(Negative Diagonal):negDiag 纵轴很好记录,但是对角线比较困难,我们先来看一下对角线的特征,假设横轴为 r 纵轴为 c , r - c 在正对角线是一致的: 斜对角线 r + c 是一致的: class Solution { public: vector<vector<string>> solveNQueens(int n) { for (int r = 0; r < n; r++) { string col = string(n, '.'); track.push_back(col); } backtracking(0, n); return res; } private: set<int> cols; // c set<int> posDiag; // r - c set<int> negDiag; // r + c vector<vector<string>> res; vector<string> track; void backtracking(int r, int n) { if (r == n) { res.push_back(track); return; } for (int c = 0; c < n; c++) { if (cols.find(c) != cols.end() || posDiag.find(r - c) != posDiag.end() || negDiag.find(r + c) != negDiag.end()) { continue; } cols.insert(c); posDiag.insert(r - c); negDiag.insert(r + c); track[r][c] = 'Q'; backtracking(r + 1, n); track[r][c] = '.'; cols.erase(c); posDiag.erase(r - c); negDiag.erase(r + c); } } }; 结果 ...
Multi-Paxios
tags: 分布式共识,Paxos
Zab
tags: 分布式共识
Paxos
tags: 分布式共识,分布式
Raft
tags: 共识算法,分布式共识
VSR
tags: Incomplete,分布式,共识算法
链式复制
tags: 分布式,Incomplete
比较-设置
利用底层指令集实现比较设置等原子操作。 See also:https://zh.wikipedia.org/wiki/%E6%AF%94%E8%BE%83%E5%B9%B6%E4%BA%A4%E6%8D%A2
全序
macOS 签名 GDB
tags: GDB,macOS macOS 下通过 GDB 调试程序会出现: Unable to find Mach task port for process-id 1375: (os/kern) failure (0x5). (please check gdb is codesigned - see taskgated(8)) 需要通过 Keychain Access Application 创建证书: code-sign-cert 需要对 gdb 进行签名,首先创建 gdb-entitlement.xml : <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.security.cs.debugger</key> <true/> </dict> 运行签名 codesign --entitlements gdb-entitlement.xml -fs code-sign-cert $(which gdb) See also: PermissionsDarwin。
偏序
偏序集合(英语:Partiallyordered set,简写poset)是数学中,特别是序理论中,指配备了部分排序关系的集合。 这 See also: 偏序关系
CAP 理论
CAP 最初作为一个经验法则提出(20 世纪 70 年代),并没有准确的定义,目的也只是帮助大家深入探讨数据库设计的权衡之道。它由 Eric Brewer 于 2000 年正式命名。 解释一 CAP 定理:不要求线性化的应用更能容忍网络故障。 只要不可靠才诶黄哦,都会发生违背线性化的风险。我们可以做如下权衡: 如果应用要求线性化,一旦发生网络分区,则必须等待网络修复,或者直接返回错误。结果为服务不可用(保证一致性或者线性化)。 如果应用不要求线性化,且每个可副本独立处理请求。此时服务可用,但结果行为不符合线性化(保证高可用)。 解释二 一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)。系统只能支持两个特性。 这里的分区指网络分区(即网络故障)。 不过,这种理解存在误导性,网络分区是一种故障,不管喜欢还是不喜欢,它都可能发生,所以无法选择或逃避分区问题。 网络正常的时候,系统可以同时保证一致性(线性化)和可用性。而一旦发生了网络故障,必须要么选择线性(一致性),要么可用性。 也就是“网络分区的情况下”是选择一致还是可用。
一致性与共识
tags: 分布式共识,一致性 一致性保证 分布式一致性主要针对延迟和故障等问题来协调副本之间的状态。 线性化:最强一致性模型 顺序保证:保证时间顺序,特别是因果关系和全局顺序 最终一致性:一种非常弱的保证,参见最终一致性效应 可线性化 分布式语义下对寄存器(单个对象)顺序的读写。应区别与可串行化。 可串行化针对不同事务的隔离,用来确保事务执行的结果与串形执行的结果相同 可线性化是读写寄存器(单个对象)的最新值的保证。 线性化依赖的条件 加锁与主节点选举 每个启动节点都试图获得锁,其中只有一个可以成功成为主节点。通过加锁来保证主节点选举「线性化」。 约束与唯一性保证 同一个用户名、电子邮件或系统中文件名需要唯一性的保证,也应该进行「线性化」。 跨通道的时间依赖 系统中存在其他通信渠道也需要「线性化」。 实现线性化系统 主从复制(部分支持可线性化) 共识算法(可线性化) 多主复制(不可线性化) 无主复制(可能不可线性化) 线性化与Quorum 一致性 Dynamo 风格的复制模型,读写遵从严格的 quorum 是无法支持可线性化的。 线性化的代价 多主复制和主从复制,网络中断都会导致同步暂停,从而无法保证客户端要求的线性化读写。 CAP 理论 可线性化与网络延迟 很少有系统真正满足线性化,现代多个 CPU 对同一个内存地址的读写都不能满足(参见硬件内存模型),如果需要强一致则需要内存屏障(栅栏)指令。 之所以放弃线性化的原因就是性能,而不是为了容错。由于网络延迟的不确定性,无论是否发生网络故障,线性化对性能的影响都是巨大的。 顺序保证 顺序与因果关系 顺序有助于保持因果关系。 因果顺序并非全序:因果关系是小范围集合的偏序,可线性化是一个全序操作。 可线性化强于因果一致性 捕获因果依赖关系:检测并发写 序列号排序 非因果序列发生器 适用于系统不存在唯一主节点。 每个节点都独立产生自己的一组序列号:一个奇数一个偶数,或者切入节点唯一标识符。 用足够高的分辨率的墙上时间戳附加到每个操作上。 预先分配区间范围,并及时扩容。 Lamport 时间戳 可以产生因果关系一致的序列号。Lamport 时间戳是一个值对 (计数器,节点 ID) : 节点 ID:每个节点都有一个唯一标志符。 计数器:每个节点都有一个计数器记录各自处理的请求总数。 优点: 两个节点可能存在相同的计数器,但是时间戳中的节点 ID 可以确保每个时间戳都是唯一的。 保证全序:比较两个 Lamport 时间戳,计数器较大的时间戳越大,计数器相同则节点 ID 大的那个时间戳越大。 通过节点排序保证了全局因果关系。Lamport 不同于版本矢量: 版本矢量用以区分两个操作是并发还是因果依赖。 Lamport 时间戳主要用于确保全序关系。 时间戳依然不够 某些场景下全序关系依然不能满足需求,比如用户名唯一性要求,为了确认用户名唯一,需要获取所有节点正在进行的请求,查看有没有相同的用户名请求,才能建立全序关系。 ...
拜占庭故障
节点撒谎伪造 Fencing 令牌,或者部分节点故障、不遵从协议、干扰网络或者恶意攻击,则为「拜占庭故障」。 如果系统仍可以继续运行,那么我们称之为「拜占庭式容错系统」。
Fencing 令牌
Fencing(围栏)锁,每次锁服务授予锁时,同时返回 fencing 令牌,每次客户端发送写请求,都必须包含所持有的 fencing 令牌。 fencing 令牌单调递增,如果低版本的写入后到达,发现已经有高版本的 fencing 令牌写入,则拒绝此次写入。
单调时钟与墙上时钟
墙上时钟 根据某个日历返回当前的日期与时间。 Linux 上的 clock_gettime(CLOCK_REALTIME) Java 中的 System.currentTimeMills() 会返回 1970-01-01(UTC)的时间戳(秒和毫秒)。 墙上时钟会和 NTP 服务器同步产生跳跃导致一些奇怪的问题。 单调时钟 更适合测量持续时间段(时间间隔),如超时或服务的响应时间。保证总是向前(不会出现墙上时钟的回拨现象)。 Linux 上的 clock_gettime(CLOCK_MONOTONIC) Java 中的 System.nanoTime() 单调时钟多个节点的对比没有任何意义,多路 CPU 可能有单独的计时器,且不与其他 CPU 进行同步。由操作系统进行补偿它们之间的偏差。 NTP 检测到本地石英比时间服务器更快或者更慢,NTP 会调整本地石英的震动频率(摆动),最大幅度为 0.05%。 NTP 并不会直接调整单调时钟向前或回拨 。
LeetCode: 47. Permutations II
tags: LeetCode,backtracking 视频解析:https://www.youtube.com/watch?v=s7AvT7cGdSo 在 LeetCode: 46. Permutations 的基础上增加重复的元素。感觉不能依赖于 track + map 的去重逻辑回溯。 class Solution { public: vector<vector<int>> permuteUnique(vector<int>& nums) { vector<vector<int>> res; } }; 数据特征: Value: 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 1, Index: 2, 1, 0, 1, 0, 2, 1, 0, 1, 0, 2, 1, 0, 1, 0, class Solution { public: vector<vector<int>> permuteUnique(vector<int>& nums) { set<vector<int>> res; vector<vector<int>> ret; int n, i; vector<vector<int>> perms; if (nums.size() == 1) { ret.push_back(nums); return ret; } for (i = 0; i < nums.size(); i++) { n = nums.back(); nums.pop_back(); perms = permuteUnique(nums); for (auto perm : perms) { perm.push_back(n); res.insert(perm); } nums.insert(nums.begin(), n); } for (auto r : res) { ret.push_back(r); } return ret; } };
分布式系统挑战
tags: 分布式 故障与部分失效 单节点一般是要么工作要么失效,但是分布式系统多节点面临部分失效,大大提高了分布式系统的复杂性。 单节点软件特性: 硬件正常工作时,相同的操作通常总会产生相同的结果,即确定性。 如果发生了某种内部错误,我们宁愿使计算机全部崩溃,而不是返回一个错误的结果。 云计算和超算 超算:垂直扩展的极端,设置检查点,一点节点故障则全部失效从上一个检查点重新开始(离线批处理),类似单机上内核崩溃。 云计算:水平扩展的极端 传统企业位于两个极端的中间 分布式可靠必然面临部分失效,需要依赖软件系统来提供容错机制。我们需要在不可靠的组件上构建可靠的系统。 不可靠网络 分布式无共享系统:成本低廉。 互联网以及大多数 IDC 内部网络都是异步网络:不保证发送一定到达(排队),等待响应时可能出现任何错误。 现实中的网络故障非常普遍 故障检测:HA、主从切换、保活机制(ICMP,SYN) 超时与无限期的延迟 网络拥塞与排队 网络负载过高会出现拥塞。 数据在发送的过程中分别会在发送端和接收端进行排队:等待发送和等待处理。 TCP 的拥塞控制机制。 虚拟化 CPU 核切换虚拟机 同步与异步网络 同步网络:固定电话网络,一路电话分配固定的电路、有带宽保证,规定延迟内保证完成数据包发送,不会丢弃数据包,成本高,利用率低 异步网络:数据中心网络,共享带宽,无法保证延迟和数据包发送,成本低廉,利用率高 不可靠时钟 单调时钟与墙上时钟 时间同步与准确性 计算机中的石英钟不够精确 NTP 服务器不稳定(网络、防火墙或服务本身) 虚拟机中时钟是虚拟化的。 终端设备不可控:休眠、故意设置 依赖同步的时钟 时钟陷阱: 一天可能不总是 86400 秒 回拨 多个节点上的时间完全不相同 需要精确同步的时钟: 自己监控所有节点上的时钟偏差 某个节点时钟漂移超出上限则将其宣告失效 时间戳与时间顺序 最后写入者获胜 时钟的置信区间 通过直接安装 GPS 接收器或原子(铯)时钟,它的误差范围通常可以查询制造商手册。 全局快照的同步时钟 Google Spanner 根据部署了 GPS 接收器或者原子时钟的 TrueTime API 返回的时钟置信区间。确保读事务足够晚发生,避免与先前事务的置信区间产生重叠。 进程暂停 垃圾回收 虚拟化暂停虚拟机 磁盘 I/O 内存交换分区 手动暂停进程(SIGSTOP/SIGCONT) 响应时间保证 RTOS 系统 调整垃圾回收的影响 知识,真相与谎言 真相由多数决定:Quorum 一致性 主节点与锁 Fencing 令牌 拜占庭故障 理论系统模型与现实 计时方面 ...
LeetCode: 46. Permutations
tags: LeetCode,backtracking Keywords backtrack 回溯算法 图解 举例: [1, 2, 3] ,顺着叶子节点和删除的节点就可以还原成全排列。 从上面图可以看出来,叶子节点加上回溯路径上被移除的节点就是结果的一项,从左到右依次是: [3,R:2,R:1] -> [3,2,1] [2,R:3,R:1] -> [2,3,1] … class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> res; vector<int> track; backtrack(res, track, nums); return res; } void backtrack(vector<vector<int>> & res, vector<int> & track, vector<int>& nums) { if (track.size() == nums.size()) { res.push_back(track); return; } for (int i = 0; i < nums.size(); i++) { if (visited.find(nums[i]) != visited.end() && visited[nums[i]]) { continue; } track.push_back(nums[i]); visited[nums[i]] = true; // go into next level backtrack(res, track, nums); visited[nums[i]] = false; track.pop_back(); } } private: map<int, bool> visited; }; 根据视频解析:https://www.youtube.com/watch?v=s7AvT7cGdSo ...
JavaScript 内存模型 (2017)
tags: 编程语言内存模型,JavaScript litmus test Litmus Test: ES2017 racy reads on ARMv8 Can this program (using atomics) see r1 = 0, r2 = 1? // Thread 1 // Thread 2 x = 1 y = 1 r1 = y x = 2 (non-atomic) r2 = x C++: yes (data race, can do anything at all). Java: the program cannot be written. ARMv8 using ldar/stlr: yes. ES2017: no! (contradicting ARMv8)
C、Rust 和 Swift 的内存模型
tags: Rust,Swift,编程语言内存模型 都采用C++11 内存模型。
C++ 弱同步原子(acquire/release atomic)
tags: C++11 内存模型,C/C++ C++ 还添加了较弱的原子,可以使用 atomic_store_explicit 和 atomic_load_explicit 以及附加的n内存排序参数来访问这些原子。使用 memory_order_seq_cst 使显式调用等效于C++ 同步原子(atomic)较短的调用。 较弱的原子称为 acquire/release 原子,一个 release 如果被后来的 acquire 观察到,那么就创建了一个 happen-before 的关系(从 release 到 acquire)。这个术语意在唤起 mutex:release 就像 unlock mutex , acquire 就像锁定同一个 mutex 。release 之前执行的写入必须对后续 acquire 之后执行的读取可见,就像解锁 mutex 之前执行的写入必须对后解锁 mutex 之后执行的读取可见一样。 atomic<int> done; // Thread 1 // Thread 2 atomic_store(&done, 1, memory_order_release); while(atomic_load(&done, memory_order_acquire) == 0) { /* loop */ } acquire/release 原子只对单个内存位置的操作进行顺序一致的交替执行,所以属于内存一致性(coherence)而非顺序一致性。 来看下面 litmus test: Litmus Test: Store Buffering Can this program see r1 = 0, r2 = 0? // Thread 1 // Thread 2 x = 1 y = 1 r1 = y r2 = x On sequentially consistent hardware: no. On x86 (or other TSO): yes! On ARM/POWER: yes! On Java (using volatiles): no. On C++11 (sequentially consistent atomics): no. On C++11 (acquire/release atomics): yes!
C++ 非同步原子(Relaxed atomic)
tags: C++11 内存模型,C/C++ C++ 并没有仅仅停留在内存一致性(coherence)的C++ 弱同步原子(acquire/release atomic)。它还引入了非同步原子,称为 relaxed 原子(memory_order_relaxed)。这些原子根本没有同步效果——它们没有创建先发生的边——并且它们根本没有排序保证。事实上,宽松原子读_写和普通读_写没有区别,除了宽松原子上的竞争不被认为是竞争, 不能着火 。
C++ 同步原子(atomic)
tags: C++11 内存模型 C++ 采用了顺序一致的原子变量,很像Java 同步原子(volatile)(与 C++ volatile 没有关系)。 atomic<int> done; // Thread 1 // Thread 2 atomic_store(&done, 1); while(atomic_load(&done) == 0) { /* loop */ } C++ 弱同步原子(acquire/release atomic)
DRF-SC 还是着火(Catch Fire)
tags: C++11 内存模型 与 Java 不同,C++ 没有给有竞争的程序任何保证。任何有竞争的程序都属于“未定义的行为”。允许在程序执行的最初几微秒内进行竞争访问,从而在几小时或几天后导致任意的错误行为。这通常被称为“DRF-SC或着火”:如果程序没有数据竞争,它以顺序一致的方式运行,如果有数据竞争,它可以做任何事情,包括着火。