{"id":4802,"date":"2016-07-02T15:12:52","date_gmt":"2016-07-02T15:12:52","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/sequential-consistency-applied\/"},"modified":"2023-06-26T12:51:35","modified_gmt":"2023-06-26T12:51:35","slug":"sequential-consistency-applied","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/sequential-consistency-applied\/","title":{"rendered":"Sequential Consistency applied"},"content":{"rendered":"<p>I have introduced In the post <a href=\"https:\/\/www.modernescpp.com\/index.php\/sequential-consistency\">Sequential Consistency <\/a>the default memory model. This model, in which all operations in all threads take place in a global time clock, has a big advantage but also a significant disadvantage.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<h2>Heavyweight synchronization<\/h2>\n<p>The significant advantage of sequential consistency is, that it matches our intuition of many threads running in parallel. The significant disadvantages are that the system has a lot&nbsp;of work to do to synchronize all the threads.<\/p>\n<p>The following program synchronizes the producer and the consumer thread with the help of sequential consistency.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ producerConsumer.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;string&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;thread&gt;<\/span>\r\n\r\nstd::string work;\r\nstd::atomic&lt;<span style=\"color: #2b91af;\">bool<\/span>&gt; ready(false);\r\n\r\n<span style=\"color: #2b91af;\">void<\/span> consumer(){\r\n  <span style=\"color: #0000ff;\">while<\/span>(!ready.load()){}\r\n  std::cout&lt;&lt; work &lt;&lt; std::endl;    \r\n}\r\n\r\n<span style=\"color: #2b91af;\">void<\/span> producer(){\r\n  work= <span style=\"color: #a31515;\">\"done\"<\/span>;\r\n  ready=true; \r\n}\r\n\r\n<span style=\"color: #2b91af;\">int<\/span> main(){\r\n  std::<span style=\"color: #0000ff;\">thread<\/span> prod(producer);\r\n  std::<span style=\"color: #0000ff;\">thread<\/span> con(consumer);\r\n  prod.join(); \r\n  con.join();\r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The output of the program is concise and exciting.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4800\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/producerConsumer.png\" alt=\"producerConsumer\" width=\"691\" height=\"154\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/producerConsumer.png 691w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/producerConsumer-300x67.png 300w\" sizes=\"auto, (max-width: 691px) 100vw, 691px\" \/><\/p>\n<p>Because of the sequential consistency, the program execution is deterministic. It always displays &#8220;done&#8221;.<\/p>\n<p>The graphic hit the spot. The consumer thread waits in the while-loop until the atomic variable <span style=\"font-family: courier new,courier;\">ready<\/span> is set to <span style=\"font-family: courier new,courier;\">true.<\/span> In case that happens, the consumer threads continue with their work.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<span style=\"background-color: #ffffff;\"><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4801\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/SequenzielleKonsistenz.png\" alt=\"SequenzielleKonsistenz\" width=\"600\" height=\"571\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/SequenzielleKonsistenz.png 752w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/SequenzielleKonsistenz-300x286.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/span><\/p>\n<p>It is easy to reason that the program will always return <span style=\"font-family: courier new,courier;\">&#8220;done&#8221;<\/span>. I have only used the two characteristics of sequential consistency. On one hand, both threads execute their instructions in the source code order, on the other hand, each thread sees the operations of the other thread in the same order. So both threads are following the same global time clock. This time clock will also hold &#8211; with the help of the <span style=\"font-family: courier new,courier;\">while(!ready.load()){}-<\/span>loop &#8211; for the synchronization of the producer and the consumer thread.<\/p>\n<p>But I can do the reasoning a lot more formally by using the terminology of the memory model. So the formal version:<\/p>\n<p><span style=\"font-family: courier new,courier;\"><span style=\"color: #ff0000;\"><strong><span style=\"font-size: 12pt;\"><span style=\"font-size: 18pt;\"> =&gt;<\/span><\/span><\/strong><\/span><\/span> Means it follows in the next lines:<\/p>\n<ol>\n<ol>\n<li><span style=\"font-family: courier new,courier;\">work= &#8220;done&#8221;<\/span>&nbsp; is <span style=\"color: #3366ff;\">sequenced-before<\/span> <span style=\"font-family: courier new,courier;\">ready=true<span style=\"color: #ff0000;\"><strong><span style=\"font-size: 12pt;\"><span style=\"font-size: 18pt;\"> =&gt;<\/span><\/span> <\/strong> <\/span> <\/span><span style=\"color: #ff0000;\"><\/span><span style=\"font-family: courier new,courier;\">work= &#8220;done&#8221;<\/span><span style=\"background-color: #ffffff; color: #3366ff;\"> happens-before<\/span> <span style=\"font-family: courier new,courier;\">ready=true<\/span><\/li>\n<li><span style=\"font-family: courier new,courier;\">while(!ready.load()){}<\/span> is <span style=\"color: #3366ff;\">sequenced-before<\/span> <span style=\"font-family: courier new,courier; color: #000000;\">std::cout&lt;&lt; work &lt;&lt; std::end<\/span><span style=\"color: #ff0000;\"><span style=\"font-family: courier new,courier; color: #000000;\">l <strong><span style=\"color: #ff0000;\"><span style=\"font-size: 12pt;\"><span style=\"font-size: 18pt;\">=&gt;<\/span><\/span>&nbsp;<\/span> <\/strong> <\/span> <\/span><span style=\"font-family: courier new,courier;\">while(!ready.load()){}<\/span><span style=\"color: #3366ff;\"> happens-before<\/span> <span style=\"font-family: courier new,courier;\">std::cout&lt;&lt; work &lt;&lt; std::endl<\/span><\/li>\n<li><span style=\"color: #000000; font-family: courier new,courier;\">ready= true<\/span><span style=\"color: #3366ff;\"> synchronizes-with<\/span> <span style=\"font-family: courier new,courier;\"><span style=\"color: #000000; font-family: courier new,courier;\">while(!ready.load()){}<\/span>&nbsp;<\/span><span style=\"color: #ff0000; font-size: 12pt;\"><strong><span style=\"font-size: 18pt;\"> =&gt;<\/span><\/strong><\/span> ready= true&nbsp;<span style=\"color: #000000; font-family: courier new,courier;\"><\/span><span style=\"font-family: courier new,courier;\"><\/span><span style=\"color: #3366ff;\">inter-thread happens-before<\/span> <span style=\"font-family: courier new,courier;\">while (!ready.load()){}&nbsp;<\/span><span style=\"color: #ff0000;\"><span style=\"font-size: 12pt;\"><strong><span style=\"font-size: 18pt;\">=&gt;<\/span><\/strong><\/span> <\/span>ready= true&nbsp;<span style=\"font-family: courier new,courier;\"><\/span><span style=\"color: #3366ff;\">happens-before <span style=\"color: #000000; font-family: courier new,courier;\">whi<\/span><\/span><span style=\"font-family: courier new,courier;\">le (!ready.load()){}<\/span><\/li>\n<\/ol>\n<\/ol>\n<p><span style=\"font-size: 14pt; color: #ff0000;\"><span style=\"font-size: 18pt;\"><strong>=&gt;<\/strong><\/span>&nbsp;<\/span> Because the happens-before relation is transitive, it follows t: <span style=\"font-family: courier new,courier;\">work= &#8220;done&#8221;<\/span> <span style=\"color: #3366ff;\">happens-before<\/span> <span style=\"font-family: courier new,courier;\">ready= true <\/span><span style=\"color: #3366ff;\">happens-before&nbsp;<\/span><span style=\"font-family: courier new,courier;\">while(!ready.load()){}<\/span> <span style=\"font-family: arial,helvetica,sans-serif; color: #3366ff;\">happens-before<\/span> <span style=\"font-family: courier new,courier;\">std::cout&lt;&lt; work &lt;&lt; std::endl<\/span><\/p>\n<\/p>\n<h2>From the sequential consistency to the acquire-release semantic<\/h2>\n<p>A thread sees the operations of another thread and all other threads in the same order. The key characteristic of sequential consistency will not hold if we use the acquire-release semantic for atomic operations. This is an area in which C# or Java will not follow. But that&#8217;s also an area where our intuition begins to wane.<\/p>\n<p>There is no global synchronization between threads in the acquire-release semantic; there is only a synchronization between atomic operations on the same atomic variable. So a write operation on one thread synchronizes with a read operation on another on the same atomic variable. This synchronization relation on the same atomic variable helps to establish a happens-before relation between atomic variables and, therefore, between threads.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>The details of the acquire-release semantic will follow in the <a href=\"https:\/\/www.modernescpp.com\/index.php\/acquire-release-semantic\">next post<\/a>. This includes an optimized spinlock.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have introduced In the post Sequential Consistency the default memory model. This model, in which all operations in all threads take place in a global time clock, has a big advantage but also a significant disadvantage.<\/p>\n","protected":false},"author":21,"featured_media":4800,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[368],"tags":[434,519],"class_list":["post-4802","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-multithreading-memory-model","tag-atomics","tag-sequential-consistency"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4802","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=4802"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4802\/revisions"}],"predecessor-version":[{"id":6967,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4802\/revisions\/6967"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/4800"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=4802"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=4802"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=4802"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}