<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
	<channel>
		<title>KAIST-CS431 on chengzhycn&#39;s blog</title>
		<link>https://blog.jinzhi.site/tags/kaist-cs431/</link>
		<description>Recent content in KAIST-CS431 on chengzhycn&#39;s blog</description>
		<generator>Hugo</generator>
		<language>en-us</language>
		
		
		
		
			<lastBuildDate>Thu, 28 Aug 2025 13:09:43 +0800</lastBuildDate>
		
			<atom:link href="https://blog.jinzhi.site/tags/kaist-cs431/index.xml" rel="self" type="application/rss+xml" />
			<item>
				<title>KAIST-CS431: Safe Memory Reclamation</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-safe-memory-reclamation/</link>
				<pubDate>Thu, 28 Aug 2025 13:09:43 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-safe-memory-reclamation/</guid>
				<description>&lt;p&gt;无锁数据结构和传统的数据结构在内存管理有一点区别是：当数据不用了，需要释放内存时，传统数据结构直接调用&lt;code&gt;free()&lt;/code&gt;就可以了，但是无锁数据结构不行。举个例子，如下图所示，T1正在读取b1的时候，T2释放了b1，T1中的指针就变成了野指针，造成不安全的内存访问。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220410114205442.png&#34; alt=&#34;image-20220410114205442&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;因此，在无锁数据结构中，内存的释放操作一般要延后执行，即保证没有线程能够访问到该片内存后，再执行内存回收。这种方式被称作Safe Memory Reclamation（SMR）。&lt;/p&gt;&#xA;&lt;p&gt;从上述例子中，我们可以从两个方面去理解一个SMR算法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;如何保护正在使用的数据不被释放？&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;什么时候能安全地释放已经被标记为需要释放的内存？&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;下面介绍两种常用的SMR算法：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Hazard Pointers（Protected Pointers）&lt;/li&gt;&#xA;&lt;li&gt;Epoch-Based Reclamation（基于代际的内存释放）&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220411074041712.png&#34; alt=&#34;image-20220411074041712&#34;&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;hazard-pointers&#34;&gt;Hazard Pointers&lt;/h2&gt;&#xA;&lt;p&gt;Hazard Pointers的基本思想是&lt;strong&gt;线程访问数据块时会将指针记录下来，其它线程尝试释放内存时会感知到数据块引用的存在，从而延迟释放内存，直到所有线程对该数据块的访问结束。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220411083606925.png&#34; alt=&#34;image-20220411083606925&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;如上图所示，使用hazard pointers有如下几个步骤：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;在线程访问共享数据时，先调用&lt;code&gt;reserve()&lt;/code&gt;方法，将指针记录在一个list内；&lt;/li&gt;&#xA;&lt;li&gt;T2线程尝试释放释放b1时，调用&lt;code&gt;retire()&lt;/code&gt;，因为检测到还有线程访问b1，延迟释放内存；&lt;/li&gt;&#xA;&lt;li&gt;因为内存没有真正释放，T1能在b1 retire后继续访问b1。访问结束后，T1调用&lt;code&gt;unreserve()&lt;/code&gt;将指针从list中移除；&lt;/li&gt;&#xA;&lt;li&gt;T3调用&lt;code&gt;gc()&lt;/code&gt;（可以周期性运行，也可以事件触发），遍历所有retired的数据块，如果list内没有指向数据块的指针，即可以真正回收内存。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Hazard Pointers有些致命的缺点：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;不够快。因为&lt;code&gt;reserve()&lt;/code&gt;保护的指针需要立即被&lt;code&gt;gc()&lt;/code&gt;感知到，所以，两个函数都需要引入开销非常大的store-load fence。在遍历整个数据结构时，每个数据块都会调用&lt;code&gt;reserve()&lt;/code&gt;，导致遍历效率降低；&lt;/li&gt;&#xA;&lt;li&gt;并不是所有的数据结构都适用Hazard Pointers。在前文的Lock-free Linked List中，我们也提过Harris-Michael‘s traversing是为了专门支持Hazard Pointers的。Harris&amp;rsquo;s traversing会retire一个连续的数据块，这样，即便T1线程保护了其中一个数据块，&lt;code&gt;gc()&lt;/code&gt;仍然可以回收其它的数据块内存，T1线程访问next指针时，仍然会出现问题。&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;epoch-based-reclamation&#34;&gt;Epoch-Based Reclamation&lt;/h2&gt;&#xA;&lt;p&gt;Hazard Pointers只保护无锁数据结构中真正被访问的那部分数据，这也导致了上文中Hazard Pointers的两个缺陷。与Hazard Pointers不同的是，&lt;strong&gt;EBR保护的是数据结构中所有可能潜在的访问&lt;/strong&gt;。这样也就不需要频繁地在线程间同步。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-safe-memory-reclamation/image-20220413090336739.png&#34; alt=&#34;image-20220413090336739&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;每个线程在访问数据结构前都会开启一个代际（epoch）。T1在代际e中retire了b1和b2。那么什么时候能安全地reclaim b1和b2的内存呢？&lt;/p&gt;&#xA;&lt;p&gt;如上图，我们在这个EBR算法中规定了一个&lt;strong&gt;代际一致性规则：并发的代际之间最多只能相差1。&lt;strong&gt;也就是说，只要T1还没有调用&lt;code&gt;set_quiescent()&lt;/code&gt;结束掉e代，那么其它线程通过&lt;code&gt;set_active()&lt;/code&gt;开启的代际号只能是e+1。而所有的e+1代的线程是有可能引用到b1和b2的。当e代结束，T2开启e+2代时，因为e+2代和e代不存在&lt;/strong&gt;同时发生&lt;/strong&gt;的可能性（通过代际一致性规则保证），所以在e+2代是不可能访问到e代retire的b1和b2的。到了e+3代，表示所有可能访问到b1和b2的e+1代都已经结束，此时即可安全地回收b1和b2内存。&lt;/p&gt;&#xA;&lt;p&gt;不同的代际一致性规则，可以安全回收的代际是不一样的。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Concurrent Data Structure</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-concurrent-data-structure/</link>
				<pubDate>Thu, 28 Aug 2025 13:09:25 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-concurrent-data-structure/</guid>
				<description>&lt;h2 id=&#34;并发数据结构的关键点&#34;&gt;并发数据结构的关键点&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;安全性：&lt;/strong&gt; 安全是并发程序对CDS的最基本的要求&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Sequential specification：多线程对数据结构的操作要能像一个队列一样&lt;/li&gt;&#xA;&lt;li&gt;Synchronization：如在栈操作中，不同的线程进行pushing和popping操作时是要同步的，不能这边线程执行完了，另外一边无法感知到这边的操作。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;通过锁或者更加底层的同步原语来保护并发数据结构&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;可扩展性：&lt;/strong&gt; 随着CPU核数/并发度的增长，有着良好的性能增长&#xA;&lt;ul&gt;&#xA;&lt;li&gt;理想情况下，性能增长应该是线性的，但现实情况往往因为各种限制因素达不到线性增长&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;通过减少锁保护范围（更细粒度的锁）来减少竞争&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;hand-over-hand locking, lock coupling, read-write locking&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;通过避免写操作降低缓存失效&lt;/strong&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Progress:&lt;/strong&gt; guaranteeing the completion (or progress) of operations&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Lock freedom: progress of at least one&lt;/li&gt;&#xA;&lt;li&gt;Wait freedom: progress of everyone&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;无锁策略&#34;&gt;无锁策略&lt;/h2&gt;&#xA;&lt;p&gt;Lock-Free 和 Wait-Free 都旨在解决传统锁（如互斥锁 mutex）带来的性能和活性问题，但采用了不同的策略和提供了不同的保证。&lt;/p&gt;&#xA;&lt;h3 id=&#34;lock-free&#34;&gt;Lock-Free&lt;/h3&gt;&#xA;&lt;p&gt;Lock-Free 是一个相对较弱的保证，但仍然非常强大和有用。它的核心思想是：&lt;strong&gt;总会有一个线程能够前进，即使其他线程被任意延迟或阻塞。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;&lt;strong&gt;核心特点：&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有死锁 (Deadlock-Free)：&lt;/strong&gt; 由于没有线程需要等待其他线程释放锁，所以不会发生死锁。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;没有活锁 (Livelock-Free)：&lt;/strong&gt; 虽然有可能发生活锁（即线程反复尝试但总是失败），但通常通过回退策略（如指数退避）或设计良好的原子操作序列可以避免。然而，严格意义上的 Lock-Free 并不直接保证 Livelock-Free。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;进度保证 (Progress Guarantee)：&lt;/strong&gt; 只要系统不是完全停滞，总有一个或多个线程可以完成操作。这意味着整个系统的吞吐量不会因为某个线程的无限期暂停而归零。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;饥饿可能 (Starvation Possible)：&lt;/strong&gt; 某个特定的线程可能会无限期地重试它的操作，而从未成功（即所谓的“饥饿”）。这是 Lock-Free 与 Wait-Free 的一个主要区别。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;实现方式：&lt;/strong&gt; 主要依赖于原子操作，如&lt;code&gt;CAS (Compare-And-Swap)&lt;/code&gt;，&lt;code&gt;FAA (Fetch-And-Add)&lt;/code&gt;，&lt;code&gt;LL/SC (Load-Link/Store-Conditional)&lt;/code&gt;等。这些操作通常由硬件提供，能够以原子方式读取、修改和写入内存位置，而无需使用操作系统级别的锁。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;常见数据结构：&lt;/strong&gt; 无锁队列 (Lock-Free Queue)，无锁栈 (Lock-Free Stack)，无锁哈希表 (Lock-Free Hash Table) 等。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;&lt;strong&gt;工作原理示例（CAS）：&lt;/strong&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Lock Implementations</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-implementations/</link>
				<pubDate>Thu, 28 Aug 2025 12:52:58 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-implementations/</guid>
				<description>&lt;h2 id=&#34;naive-spin-lock&#34;&gt;Naive Spin Lock&lt;/h2&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220324081153922.png&#34; alt=&#34;image-20220324081153922&#34;&gt;&lt;/p&gt;&#xA;&lt;h3 id=&#34;naive-spin-lock的缺陷&#34;&gt;Naive Spin Lock的缺陷&lt;/h3&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;无法保证公平性，可能有的倒霉蛋空转了一辈子也无法cas成功，无法做到按竞争线程先来后到的次序占有锁。&lt;/li&gt;&#xA;&lt;li&gt;扩展性差，大量对同一内存区域的自旋引发性能问题。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;对naive-spin-lock的优化&#34;&gt;对Naive Spin Lock的优化&lt;/h2&gt;&#xA;&lt;p&gt;关键思路：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过release/acquire同步机制保证互斥量的排他性&#xA;&lt;ul&gt;&#xA;&lt;li&gt;从一个临界区的结束（release）到另一个临界区的开始（acquire）&lt;/li&gt;&#xA;&lt;li&gt;在ticket lock里面是&lt;code&gt;curr&lt;/code&gt;成员，CLH/MCS lock是每个waiter的一个新内存区域&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;通过排队和等待不同的区域释放保证公平性&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过公平的指令来排队（如swap, fetch-and-add）&lt;/li&gt;&#xA;&lt;li&gt;Ticket lock：通过&lt;code&gt;next&lt;/code&gt;来排队，&lt;code&gt;curr&lt;/code&gt;来等待锁&lt;/li&gt;&#xA;&lt;li&gt;CLH/MCS lock：通过&lt;code&gt;tail&lt;/code&gt;来排队，等待每个锁调用者申请的不同区域中值的变化来获取锁&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;ticket-lock&#34;&gt;Ticket Lock&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220323075751550.png&#34; alt=&#34;image-20220323075751550&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;ticket lock在锁的结构体里面新增了一个原子变量&lt;code&gt;next&lt;/code&gt;，每次需要竞争锁时，需要先从&lt;code&gt;next&lt;/code&gt;中拿到一个ticket，然后再去监听&lt;code&gt;curr&lt;/code&gt;，只有&lt;code&gt;curr&lt;/code&gt;被更新成当前的ticket值后，才能去占领锁。&lt;/p&gt;&#xA;&lt;h4 id=&#34;优点&#34;&gt;优点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;利用公平指令排队解决了公平性问题&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;缺点&#34;&gt;缺点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;API相对较复杂（调用者感知ticket）（为什么不直接用curr+1？）&lt;/li&gt;&#xA;&lt;li&gt;没有解决spinlock中所有线程监听同一个原子变量的问题&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;clh-lock&#34;&gt;CLH Lock&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220323082114800.png&#34; alt=&#34;image-20220323082114800&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;为了减少缓存一致性带来的开销，CLH lock被发明了。CLH是三个人首字母的缩写：Craig, Landin, and Hagersten。&lt;/p&gt;&#xA;&lt;p&gt;CLH lock给所有等待线程分配了一个Node，每个Node初始为true，在锁内维护一个链表，所有竞争锁的线程都会获取到前一个线程的Node，并将&lt;code&gt;tail&lt;/code&gt;指针指向自己的Node。&lt;/p&gt;&#xA;&lt;p&gt;不同于spin lock和Ticket lock，CLH lock的临界区是前一个Node中的原子变量。在锁释放时，当前线程会将自己的Node值置为false，而排队的下一个线程监听到这个变化，则可以安全地占有锁了。&lt;/p&gt;&#xA;&lt;h4 id=&#34;优点-1&#34;&gt;优点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;线程监听的不同临界区，减少缓存一致性的开销&lt;/li&gt;&#xA;&lt;li&gt;链表排队，也能保证公平性&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h4 id=&#34;缺点-1&#34;&gt;缺点&lt;/h4&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;O(n)的空间复杂度开销，n是临界区数目&lt;/li&gt;&#xA;&lt;li&gt;每个线程都是在前驱节点的Node上自旋，如果跨NUMA会有性能问题&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;mcs-lock&#34;&gt;MCS Lock&lt;/h3&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-lock-implementations/image-20220324080334262.png&#34; alt=&#34;image-20220324080334262&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;MCS lock也是以三个人名命名的：John M. Mellor-Crummey and Michael L. Scott。MCS lock和LCH lock最大的不同是：CLH lock是在前驱节点上自旋，MCS则是在自己节点上自旋。&lt;/p&gt;&#xA;&lt;p&gt;在CLH的Node结构里面，MCS又添加了一个&lt;code&gt;next&lt;/code&gt;字段，新的线程竞争锁时，会将自己的Node添加到前驱节点的&lt;code&gt;next&lt;/code&gt;字段中。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Promising Semantics</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-promising-semantics/</link>
				<pubDate>Wed, 27 Aug 2025 13:40:27 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-promising-semantics/</guid>
				<description>&lt;p&gt;本章是基于作者的研究“Promising semantics”: &lt;a href=&#34;https://sf.snu.ac.kr/promise-concurrency/&#34;&gt;https://sf.snu.ac.kr/promise-concurrency/&lt;/a&gt; ，提出的一种对宽松内存（relaxed-memory）并发的建模方法。&lt;/p&gt;&#xA;&lt;p&gt;主要观点有4个：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;modeling load hoisting w/ &lt;strong&gt;multi-valued memory&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;允许一个线程从某个位置读取到一个旧值&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;modeling read-modify-write w/ &lt;strong&gt;message adjacency&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;禁止对单个值同时进行多个read-modify-write操作&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;modeling coherence &amp;amp; ordering w/ &lt;strong&gt;views&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;限制线程的行为&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;modeling store hoisting w/ &lt;strong&gt;promises&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Allowing a thread to speculatively write a value&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;个人理解，**即便是编译器/硬件的指令重排，也是需要遵循一定的规则的，不能随意乱排。**作者从值读取、存储、read-modify-write多种角度对线程的行为进行了建模，是为了解释哪些情况下出现多线程执行出现哪些结果是可能的，哪些是不被允许的。&lt;/p&gt;&#xA;&lt;p&gt;hoisting load/store在网上没有搜到解释，但是有个gcc的优化提到了这个概念。大概意思时将存值/取值操作从原先的指令顺序中调整位置，优化执行效率。&lt;/p&gt;&#xA;&lt;p&gt;&lt;a href=&#34;https://gcc.gnu.org/news/hoist.html&#34;&gt;Load/Store Hoisting - GNU Project&lt;/a&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;multi-value-memory&#34;&gt;multi-value memory&lt;/h2&gt;&#xA;&lt;p&gt;内存是一系列消息（message）所在的位置，而消息可以看作是&lt;strong&gt;值和时间戳&lt;/strong&gt;的组合。线程很有可能在读取时从内存中读到一个旧值。（effectively hoisting loads）&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-promising-semantics/image-20220318080820894.png&#34; alt=&#34;image-20220318080820894&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;在作者举的例子中，r1=r2=0是被允许的，因为r1 r2都有可能从Y X中读到一个旧值。从后文的view角度理解，因为在独立的线程中，X和Y的赋值并没有改变当前线程中相应Y和X的view，所以，r1 r2的读取操作是可以读到旧值的。&lt;/p&gt;&#xA;&lt;h2 id=&#34;message-adjacency&#34;&gt;message adjacency&lt;/h2&gt;&#xA;&lt;p&gt;上面说了，消息是值和时间戳范围的组合。read-modify-write操作修改了消息的值，在时间轴上应该和前值紧邻在一起（no gap）。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://blog.jinzhi.site/images/notes/kaist-cs431-promising-semantics/image-20220318081606125.png&#34; alt=&#34;image-20220318081606125&#34;&gt;&lt;/p&gt;&#xA;&lt;p&gt;可以看到，两次fetch_add操作后，从X的视角上看，0、1、2是紧邻的。第二次fetch_add操作，只能紧贴着1操作，而不能插到0和1之间。&lt;/p&gt;&#xA;&lt;h2 id=&#34;views&#34;&gt;views&lt;/h2&gt;&#xA;&lt;p&gt;这个semantics对我是启发性最强的一章。&lt;/p&gt;&#xA;&lt;p&gt;multi-valued memory允许了太多不在预期中的行为，因此我们需要做些限制，保证一致性和同步。&lt;/p&gt;&#xA;&lt;p&gt;View分为三种，分别是：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Per-thread view：一致性&lt;/li&gt;&#xA;&lt;li&gt;Per-message view：release/acquire同步&lt;/li&gt;&#xA;&lt;li&gt;Global view：SC同步&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;per-thread-view&#34;&gt;Per-thread view&lt;/h3&gt;&#xA;&lt;p&gt;Per-thread view表示线程对消息的确认。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Nondeterminisms of Shared-memory Concurrency</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-nondeterminisms-of-shared-memory-concurrency/</link>
				<pubDate>Wed, 27 Aug 2025 13:31:59 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-nondeterminisms-of-shared-memory-concurrency/</guid>
				<description>&lt;h2 id=&#34;nondeterminism&#34;&gt;Nondeterminism&lt;/h2&gt;&#xA;&lt;h3 id=&#34;thread-interleaving&#34;&gt;thread interleaving&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;interleaving semantics：&lt;/strong&gt; 将多线程的指令交替组合成好像是单线程执行一样。&lt;/p&gt;&#xA;&lt;p&gt;不同线程间的Load/store指令是穿插执行的，导致最终的行为有多种多样的可能。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;static&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;AtomicUsize&lt;/span&gt;::&lt;span class=&#34;n&#34;&gt;new&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// thread A &amp;amp; B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;load&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;store&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如上示例，两个线程A和B同时对COUNTER进行+1操作，预期结果当然是2。但是&lt;strong&gt;由于线程调度的不确定性&lt;/strong&gt;可能出现如下的执行顺序：&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[COUNTER=0] A load, B load, A store, B store [COUNTER=1]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;导致结果不符合预期。&lt;/p&gt;&#xA;&lt;h4 id=&#34;解决方案&#34;&gt;解决方案&lt;/h4&gt;&#xA;&lt;p&gt;使用原子的reading &amp;amp; writing，禁止掉这种不符预期的执行顺序。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-rust&#34; data-lang=&#34;rust&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// thread A &amp;amp; B&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;let&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;c&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;no&#34;&gt;COUNTER&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fetch_and_add&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;“Read-modify-write”&lt;/strong&gt;, e.g. swap, compare-and-swap, fetch-and-add&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;reordering&#34;&gt;reordering&lt;/h3&gt;&#xA;&lt;p&gt;&lt;strong&gt;同一个线程中的指令会因为硬件和编译器的优化发生指令重排。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;DATA = 42;       ||   if FLAG.load() {&#xA;FLAG.store(1);   ||       assert(DATA == 42);&#xA;                 ||   }&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;如上图示例，预期是当&lt;code&gt;FLAG.load()&lt;/code&gt;为1时，&lt;code&gt;DATA == 42&lt;/code&gt;。但是因为指令重排，左边线程中，&lt;code&gt;FLAG.store(1)&lt;/code&gt;可能发生在赋值语句前面；右边线程中assert语句也可能发生在if语句前面。&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Lock Based API</title>
				<link>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-based-api/</link>
				<pubDate>Wed, 27 Aug 2025 12:58:54 +0800</pubDate>
				<guid>https://blog.jinzhi.site/posts/2025-08/kaist-cs431-lock-based-api/</guid>
				<description>&lt;h2 id=&#34;标准库中的并发api&#34;&gt;标准库中的并发API&lt;/h2&gt;&#xA;&lt;p&gt;Rust 标准库中基于锁的 API 主要围绕几个核心原语构建，这些原语提供了不同级别的并发控制和用途。它们都包含在 &lt;code&gt;std::sync&lt;/code&gt; 模块中。以下是一些最常用的基于锁的 API：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::Mutex&amp;lt;T&amp;gt;&lt;/code&gt; (互斥锁)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 最常见的互斥锁，用于保护共享数据，确保一次只有一个线程可以访问该数据。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;当线程尝试获取已被锁定的 Mutex 时，它会阻塞直到锁被释放。&lt;/li&gt;&#xA;&lt;li&gt;提供内部可变性（&lt;code&gt;&amp;amp;T&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;amp;mut T&lt;/code&gt;）通过 RAII (Resource Acquisition Is Initialization) 机制，即 &lt;code&gt;MutexGuard&lt;/code&gt;。当 &lt;code&gt;MutexGuard&lt;/code&gt; 离开作用域时，锁会自动释放。&lt;/li&gt;&#xA;&lt;li&gt;是“poisoning”感知的：如果持有锁的线程在锁被释放前发生 panic，&lt;code&gt;Mutex&lt;/code&gt; 会被标记为 poisoned。后续尝试获取锁的线程会得到一个 &lt;code&gt;PoisonError&lt;/code&gt;，其中包含原始的 &lt;code&gt;MutexGuard&lt;/code&gt;，允许它们决定如何处理被中断的数据。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 当你需要独占访问某个共享资源时，例如全局计数器、数据结构或配置设置。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::RwLock&amp;lt;T&amp;gt;&lt;/code&gt; (读写锁)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 允许多个读取者同时访问共享数据，但只允许一个写入者独占访问数据。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;读取者（&lt;code&gt;read()&lt;/code&gt;）：&lt;/strong&gt; 允许多个线程并行获取读锁。只要没有写入者持有锁，所有读锁请求都会成功。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;写入者（&lt;code&gt;write()&lt;/code&gt;）：&lt;/strong&gt; 只允许一个线程获取写锁。当有写入者持有锁时，所有读锁和写锁请求都会阻塞。&lt;/li&gt;&#xA;&lt;li&gt;也提供 RAII 机制，通过 &lt;code&gt;RwLockReadGuard&lt;/code&gt; 和 &lt;code&gt;RwLockWriteGuard&lt;/code&gt;。&lt;/li&gt;&#xA;&lt;li&gt;同样是“poisoning”感知的。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 当你的数据被频繁读取但很少写入时，&lt;code&gt;RwLock&lt;/code&gt; 可以提供比 &lt;code&gt;Mutex&lt;/code&gt; 更好的并发性能。例如，一个缓存系统或一个配置对象。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::Once&lt;/code&gt; (只运行一次)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 确保某个代码块（一个初始化函数）在程序生命周期中只被执行一次，即使有多个线程同时尝试触发它。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;code&gt;call_once()&lt;/code&gt; 方法会执行一个闭包。第一次调用会实际执行闭包，后续的调用会等待第一次调用完成，但不会再次执行闭包。&lt;/li&gt;&#xA;&lt;li&gt;通常用于惰性初始化全局数据或单例模式。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 初始化全局静态变量（例如日志系统、配置加载器）或实现单例模式。通常与 &lt;code&gt;lazy_static&lt;/code&gt; crate (在稳定版 Rust 中) 或 &lt;code&gt;std::sync::OnceLock&lt;/code&gt; (在 1.70+ 版本中，见下文) 结合使用。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;std::sync::Barrier&lt;/code&gt; (屏障)&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;用途：&lt;/strong&gt; 用于同步一组线程，确保所有线程都到达某个预定义点后才能继续执行。&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;特点：&lt;/strong&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;通过 &lt;code&gt;wait()&lt;/code&gt; 方法实现等待。当调用 &lt;code&gt;wait()&lt;/code&gt; 的线程数量达到预设值时，所有等待的线程都会同时被释放。&lt;/li&gt;&#xA;&lt;li&gt;&lt;code&gt;wait()&lt;/code&gt; 返回一个 &lt;code&gt;BarrierWaitResult&lt;/code&gt;，指示当前线程是否是最后一个到达屏障的。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;何时使用：&lt;/strong&gt; 需要协调多个并行任务的执行，例如在某个阶段结束后开始下一阶段，或者在所有子任务完成后进行汇总。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;hr&gt;&#xA;&lt;p&gt;&lt;strong&gt;Rust 1.70+ 中引入的更现代的基于锁的 API：&lt;/strong&gt;&lt;/p&gt;</description>
			</item>
			<item>
				<title>KAIST-CS431: Lock</title>
				<link>https://blog.jinzhi.site/posts/2022-03/kaist-cs431-lock/</link>
				<pubDate>Tue, 08 Mar 2022 00:00:00 +0000</pubDate>
				<guid>https://blog.jinzhi.site/posts/2022-03/kaist-cs431-lock/</guid>
				<description>&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Pros &amp;amp; cons:&lt;/strong&gt; simple &amp;amp; possibly inefficient&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;low-level-lock-api&#34;&gt;Low-Level Lock API&lt;/h2&gt;&#xA;&lt;p&gt;常用的low-level锁的API有：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lock.acquire()&lt;/strong&gt;: 阻塞，直到获取到锁&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lock.try_acquire():&lt;/strong&gt; 返回锁是否已经被占用了，如果是，返回false，否，占用锁并返回true。不阻塞&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Lock.release()&lt;/strong&gt;: 释放锁&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c++&#34; data-lang=&#34;c++&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;L&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acquire&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;r1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;L&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;L&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;acquire&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;r2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;X&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;X&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;r2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;L&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;release&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;();&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但是，这些API给用户在使用时造成了很多挑战（心智负担）：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Relating lock and resource&lt;/strong&gt;: 用户只有在拿到锁时才能访问被保护的变量；&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Matching acquire/release&lt;/strong&gt;: 用户只能释放已经拿到的锁。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;如果锁没有得到正确的处理，会造成很多潜在的问题，并且，这些问题很难发现。因此，在并发编程时，low-level的锁API存在如下问题：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;High cost&lt;/strong&gt;：程序员需要始终关注API的使用；&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Potential bugs&lt;/strong&gt;：不正确的使用容易造成很多潜在的bugs。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;high-level-lock-api&#34;&gt;High-Level Lock API&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;想要一个易用地，始终能保证安全地high-level API。&#xA;&lt;ul&gt;&#xA;&lt;li&gt;Acquire/release自动匹配；&lt;/li&gt;&#xA;&lt;li&gt;Lock和resource显式关联。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;C++中，这种API被称作&lt;strong&gt;RAII&lt;/strong&gt;：&lt;strong&gt;Resource Acquisition Is Initialization&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-c++&#34; data-lang=&#34;c++&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;string&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;mutex&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;fstream&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;cp&#34;&gt;#include&lt;/span&gt; &lt;span class=&#34;cpf&#34;&gt;&amp;lt;stdexcept&amp;gt;&lt;/span&gt;&lt;span class=&#34;cp&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kt&#34;&gt;void&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;write_to_file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;string&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;message&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 创建关于文件的互斥锁&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;static&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mutex&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 在访问文件前进行加锁&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lock_guard&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;lock&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;mutex&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 尝试打开文件&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;ofstream&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;example.txt&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;file&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_open&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;throw&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;runtime_error&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;unable to open file&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 输出文件内容&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;file&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;message&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;std&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;::&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;endl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 当离开作用域时，文件句柄会被首先析构 (不管是否抛出了异常)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;c1&#34;&gt;// 互斥锁也会被析构 (同样地，不管是否抛出了异常)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;RAII要求，资源的有效期与持有资源的&lt;a href=&#34;https://zh.wikipedia.org/w/index.php?title=%e5%af%b9%e8%b1%a1%e7%9a%84%e7%94%9f%e5%91%bd%e6%9c%9f&amp;amp;action=edit&amp;amp;redlink=1&#34;&gt;对象的生命期&lt;/a&gt;严格绑定，即由对象的&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0&#34;&gt;构造函数&lt;/a&gt;完成&lt;a href=&#34;https://zh.wikipedia.org/w/index.php?title=%e8%b5%84%e6%ba%90%e7%9a%84%e5%88%86%e9%85%8d&amp;amp;action=edit&amp;amp;redlink=1&#34;&gt;资源的分配&lt;/a&gt;（获取），同时由&lt;a href=&#34;https://zh.wikipedia.org/wiki/%E6%9E%90%E6%9E%84%E5%87%BD%E6%95%B0&#34;&gt;析构函数&lt;/a&gt;完成资源的释放。在这种要求下，只要对象能正确地析构，就不会出现&lt;a href=&#34;https://zh.wikipedia.org/w/index.php?title=%e8%b5%84%e6%ba%90%e6%b3%84%e9%9c%b2&amp;amp;action=edit&amp;amp;redlink=1&#34;&gt;资源泄露&lt;/a&gt;问题。&lt;/p&gt;</description>
			</item>
	</channel>
</rss>
