{"id":5438,"date":"2018-05-11T16:47:00","date_gmt":"2018-05-11T16:47:00","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-sharing-data-between-threads\/"},"modified":"2023-06-26T11:51:05","modified_gmt":"2023-06-26T11:51:05","slug":"c-core-guidelines-sharing-data-between-threads","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/c-core-guidelines-sharing-data-between-threads\/","title":{"rendered":"C++ Core Guidelines: Sharing Data between Threads"},"content":{"rendered":"<p>If you want to have fun with threads, you should share mutable data between them. To get no data race and, therefore, undefined behavior, you have to think about the synchronization of your threads.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5436\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/05\/retriever.jpg\" alt=\"retriever\" style=\"display: block; margin-left: auto; margin-right: auto;\" width=\"640\" height=\"426\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/05\/retriever.jpg 640w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/05\/retriever-300x200.jpg 300w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>The three rules in this post may be quite obvious for the experienced multithreading developer but crucial for the novice in the multithreading domain. Here are they:<\/p>\n<ul>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-raii\">CP.20: Use RAII, never plain <code class=\"highlighter-rouge no-highlight\">lock()<\/code>\/<code class=\"highlighter-rouge no-highlight\">unlock()<\/code><\/a><\/li>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-lock\">CP.21: Use <code class=\"highlighter-rouge no-highlight\">std::lock()<\/code> or <code class=\"highlighter-rouge no-highlight\">std::scoped_lock<\/code> to acquire multiple <code class=\"highlighter-rouge no-highlight\">mutex<\/code>es<\/a><\/li>\n<li><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-unknown\">CP.22: Never call unknown code while holding a lock (e.g., a callback)<\/a><\/li>\n<\/ul>\n<p>&nbsp;Let&#8217;s start with the most obvious rule.<\/p>\n<h2><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-raii\">CP.20: Use RAII, never plain <code class=\"highlighter-rouge no-highlight\">lock()<\/code>\/<code class=\"highlighter-rouge no-highlight\">unlock()<\/code><\/a><\/h2>\n<p><strong>No naked mutex!<\/strong> Put your mutex always in a lock. The lock will automatically release (unlock) the mutex if it goes out of scope.&nbsp;RAII stands for <strong>R<\/strong>esource <strong>A<\/strong>cquisition <strong>I<\/strong>s <strong>I<\/strong>nitialization and means<strong> that you bind a resource&#8217;s lifetime to a local variable&#8217;s lifetime. C++ automatically manages the lifetime of locals.&nbsp; <br \/><\/strong><\/p>\n<p><span style=\"font-family: courier new, courier;\">std::lock_guard, std::unique_lock, std::shared_lock<\/span> (C++14), or <span style=\"font-family: courier new, courier;\">std::std::scoped_lock<\/span> (C++17) implement this pattern but also the smart pointers <span style=\"font-family: courier new, courier;\">std::unique_ptr<\/span>, and <span style=\"font-family: courier new, courier;\">std::shared_ptr<\/span>. My previous post <a href=\"https:\/\/www.modernescpp.com\/index.php\/component\/jaggyblog\/garbage-collectio-no-thanks\">Garbage Collection &#8211; No Thanks<\/a> explains the details to RAII.<\/p>\n<p>What does this mean for your multithreading code?<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std<span style=\"color: #555555;\">::<\/span>mutex mtx;\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">do_stuff<\/span>()\r\n{\r\n    mtx.lock();\r\n    <span style=\"color: #0099ff; font-style: italic;\">\/\/ ... do stuff ... (1)<\/span>\r\n    mtx.unlock();\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>It doesn&#8217;t matter if an exception occurs in (1) or you just forgot to unlock the <span style=\"font-family: courier new, courier;\">mtx<\/span>; in both cases, you will get a deadlock if another thread wants to acquire (lock) the <span style=\"font-family: courier new, courier;\">std::mutex mtx<\/span>. The rescue is quite apparent.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std<span style=\"color: #555555;\">::<\/span>mutex mtx;\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">do_stuff<\/span>()\r\n{\r\n    std<span style=\"color: #555555;\">::<\/span>lock_guard<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> lck {mtx};\r\n    <span style=\"color: #0099ff; font-style: italic;\">\/\/ ... do stuff ...<\/span>\r\n}                 <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Put the mutex into a lock, and the mutex will automatically be unlocked at (1) because the <span style=\"font-family: courier new, courier;\">lck<\/span> goes out of scope.<\/p>\n<\/p>\n<h2><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-lock\">CP.21: Use <code class=\"highlighter-rouge no-highlight\">std::lock()<\/code> or <code class=\"highlighter-rouge no-highlight\">std::scoped_lock<\/code> to acquire multiple <code class=\"highlighter-rouge no-highlight\">mutex<\/code>es<\/a><\/h2>\n<p>If a thread needs more than one mutex, you must be extremely careful that you lock the mutexes in the same sequence. If not, a bad interleaving of threads may cause a deadlock. The following program causes a deadlock.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ lockGuardDeadlock.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;chrono&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;mutex&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;thread&gt;<\/span>\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">struct<\/span> CriticalData{\r\n  std<span style=\"color: #555555;\">::<\/span>mutex mut;\r\n};\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">deadLock<\/span>(CriticalData<span style=\"color: #555555;\">&amp;<\/span> a, CriticalData<span style=\"color: #555555;\">&amp;<\/span> b){\r\n\r\n  std<span style=\"color: #555555;\">::<\/span>lock_guard<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span>guard1(a.mut);           <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)        <\/span>\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Thread: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span>  std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n  std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>sleep_for(std<span style=\"color: #555555;\">::<\/span>chrono<span style=\"color: #555555;\">::<\/span>milliseconds(<span style=\"color: #ff6600;\">1<\/span>));\r\n \r\n  std<span style=\"color: #555555;\">::<\/span>lock_guard<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span>guard2(b.mut);          <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">\"Thread: \"<\/span> <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>this_thread<span style=\"color: #555555;\">::<\/span>get_id() <span style=\"color: #555555;\">&lt;&lt;<\/span>  std<span style=\"color: #555555;\">::<\/span>endl;\r\n  \r\n  <span style=\"color: #0099ff; font-style: italic;\">\/\/ do something with a and b (critical region)        (3)<\/span>\r\n}\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>(){\r\n\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n  CriticalData c1;\r\n  CriticalData c2;\r\n\r\n  std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t1([<span style=\"color: #555555;\">&amp;<\/span>]{deadLock(c1, c2);});            <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n  std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> t2([<span style=\"color: #555555;\">&amp;<\/span>]{deadLock(c2, c1);});            <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n\r\n  t1.join();\r\n  t2.join();\r\n\r\n  std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> std<span style=\"color: #555555;\">::<\/span>endl;\r\n\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>Threads<span style=\"font-family: courier new, courier;\"> t1<\/span> and<span style=\"font-family: courier new, courier;\"> t2<\/span> need two resources <span style=\"font-family: courier new, courier;\">CriticalData<\/span>, to perform their job (3). <span style=\"font-family: courier new, courier;\">CriticalData<\/span> has its own mutex <span style=\"font-family: courier new, courier;\">mut<\/span> to synchronize the access. Unfortunately, both invoke the function <span style=\"font-family: courier new, courier;\">deadlock<\/span> with the arguments <span style=\"font-family: courier new, courier;\">c1<\/span> and<span style=\"font-family: courier new, courier;\"> c2<\/span> in a different sequence (1). Now we have a race condition. If thread <span style=\"font-family: courier new, courier;\">t1<\/span> can lock the first mutex<span style=\"font-family: courier new, courier;\"> a.mut<\/span> but not the second one <span style=\"font-family: courier new, courier;\">b.mu<\/span>t because, in the meantime, thread <span style=\"font-family: courier new, courier;\">t2<\/span> locks the second one, we will get a deadlock (2).<\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5437\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/05\/lockGuardDeadlock.png\" alt=\"lockGuardDeadlock\" style=\"display: block; margin-left: auto; margin-right: auto;\" width=\"453\" height=\"206\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/05\/lockGuardDeadlock.png 453w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2018\/05\/lockGuardDeadlock-300x136.png 300w\" sizes=\"auto, (max-width: 453px) 100vw, 453px\" \/><\/p>\n<p>The easiest way to solve the deadlock is to lock both mutexes atomically.<\/p>\n<p>With C++11, you can use a <span style=\"font-family: courier new, courier;\">std::unique_lock<\/span> together with <span style=\"font-family: courier new, courier;\">std::lock<\/span>. <span style=\"font-family: courier new, courier;\">std::unique_lock<\/span>, you can defer the locking of its mutex. The function <span style=\"font-family: courier new, courier;\">std::lock<\/span>, which can lock an arbitrary number of mutexes in an atomic way, does the locking finally.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">deadLock<\/span>(CriticalData<span style=\"color: #555555;\">&amp;<\/span> a, CriticalData<span style=\"color: #555555;\">&amp;<\/span> b){\r\n    std<span style=\"color: #555555;\">::<\/span>unique_lock<span style=\"color: #555555;\">&lt;<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> guard1(a.mut, std<span style=\"color: #555555;\">::<\/span>defer_lock);\r\n    std<span style=\"color: #555555;\">::<\/span>unique_lock<span style=\"color: #555555;\">&lt;<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> guard2(b.mut, std<span style=\"color: #555555;\">::<\/span>defer_lock);\r\n    std<span style=\"color: #555555;\">::<\/span>lock(guard1, guard2);\r\n    <span style=\"color: #0099ff; font-style: italic;\">\/\/ do something with a and b (critical region)<\/span>\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>With C++17, a<span style=\"font-family: courier new, courier;\"> std::scoped_lock<\/span> can lock an arbitrary number of mutex in one atomic operation.<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">deadLock<\/span>(CriticalData<span style=\"color: #555555;\">&amp;<\/span> a, CriticalData<span style=\"color: #555555;\">&amp;<\/span> b){\r\n    std<span style=\"color: #555555;\">::<\/span>scoped_lock(a.mut, b.mut);\r\n    <span style=\"color: #0099ff; font-style: italic;\">\/\/ do something with a and b (critical region<\/span>\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<h2><a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#Rconc-unknown\">CP.22: Never call unknown code while holding a lock (e.g., a callback)<\/a><\/h2>\n<p>Why is this code snippet bad?<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><br \/>std::mutex m;<br \/>{<span style=\"color: #555555;\"><\/span>\r\n    std<span style=\"color: #555555;\">::<\/span>lock_guard<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> lockGuard(m);\r\n    sharedVariable <span style=\"color: #555555;\">=<\/span> unknownFunction();\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>I can only speculate about the <span style=\"font-family: courier new, courier;\">unknownFunction<\/span>. If <span style=\"font-family: courier new, courier;\">unknownFunction<\/span><\/p>\n<ul>\n<li>tries to lock the mutex <span style=\"font-family: courier new, courier;\">m<\/span>, that will be undefined behavior. Most of the time, you will get a deadlock.<\/li>\n<li>starts a new thread that tries to lock the mutex <span style=\"font-family: courier new, courier;\">m<\/span>, you will get a deadlock.<\/li>\n<li>locks another mutex<span style=\"font-family: courier new, courier;\"> m2<\/span>, you may get a deadlock because you lock the two mutexes <span style=\"font-family: courier new, courier;\">m<\/span> and <span style=\"font-family: courier new, courier;\">m2<\/span> simultaneously. Now another thread may lock the same mutexes in a different sequence.<\/li>\n<li>will not directly or indirectly try to lock the mutex <span style=\"font-family: courier new, courier;\">m;<\/span> all seems fine. \u201cSeems\u201d because your coworker can modify the function, or the function is dynamically linked, and you get a different version. All bets are open to what may happen.<\/li>\n<li>work as expected, you may have a performance problem because you don\u2019t know how long the function <span style=\"font-family: courier new, courier;\">unknownFunction<\/span> would take. What is meant to be a multithreaded program may become a single-threaded program.<\/li>\n<\/ul>\n<p>To solve these issues, use a local variable:<\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std::mutex m;<br \/><span style=\"color: #006699; font-weight: bold;\">auto<\/span> tempVar <span style=\"color: #555555;\">=<\/span> unknownFunction();\r\n<span style=\"color: #555555;\"><\/span>{\r\n    std<span style=\"color: #555555;\">::<\/span>lock_guard<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> lockGuard(m);\r\n    sharedVariable <span style=\"color: #555555;\">=<\/span> tempVar;\r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>This additional indirection solves all issues. <span style=\"font-family: courier new, courier;\">tempVar<\/span> is a local variable and can not be a victim of a data race. This means that you can invoke <span style=\"font-family: courier new, courier;\">unknownFunction<\/span> without a synchronization mechanism. Additionally, the time for holding a lock is reduced to its bare minimum: assigning the value of <span style=\"font-family: courier new, courier;\">tempVar<\/span> to <span style=\"font-family: courier new, courier;\">sharedVariable<\/span>.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>If you don&#8217;t call join or detach on your created thread child, the child will throw a <span style=\"font-family: courier new, courier;\">std::terminate<\/span> exception in its destructor. <code>std::terminate<\/code><span class=\"t-lc\"><\/span>calls per default <span class=\"t-lc\"><a href=\"http:\/\/en.cppreference.com\/w\/cpp\/utility\/program\/abort\" title=\"cpp\/utility\/program\/abort\">std::abort<\/a><\/span>. To overcome this issue, the <a href=\"http:\/\/isocpp.github.io\/CppCoreGuidelines\/CppCoreGuidelines#S-gsl\">guidelines support library<\/a> has a<span style=\"font-family: courier new, courier;\">&nbsp;gsl::joining_thread<\/span> which calls join at the end of its scope. I will have a closer look at <span style=\"font-family: courier new, courier;\">gsl::joining_thread<\/span> in my <a href=\"https:\/\/goo.gl\/bD6gnk\">next post<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you want to have fun with threads, you should share mutable data between them. To get no data race and, therefore, undefined behavior, you have to think about the synchronization of your threads.<\/p>\n","protected":false},"author":21,"featured_media":5436,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[372],"tags":[430,433],"class_list":["post-5438","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-modern-c","tag-lock","tag-mutex"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5438","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=5438"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5438\/revisions"}],"predecessor-version":[{"id":6827,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5438\/revisions\/6827"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5436"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}