{"id":3444,"date":"2019-11-14T14:22:21","date_gmt":"2019-11-14T14:22:21","guid":{"rendered":"http:\/\/www.max-sperling.bplaced.net\/?p=3444"},"modified":"2024-02-16T10:20:08","modified_gmt":"2024-02-16T10:20:08","slug":"problems-happening-with-multithreading-c","status":"publish","type":"post","link":"http:\/\/www.max-sperling.bplaced.net\/?p=3444","title":{"rendered":"Problems happening with multithreading (C++)"},"content":{"rendered":"<p>The two most common problems that are happing when dealing with multithreading are the deadlock and the race condition. Follow this <a href=\"http:\/\/www.max-sperling.bplaced.net\/?p=1855\">Link<\/a> to get an easy code example for both of them.<\/p>\n<hr>\n<p><strong>Deadlock<\/strong><br \/>\nA deadlock is happening when a thread keeps a lock constantly. All threads that have to excute the still locked code parts are getting blocked. The user will normaly experience this problem with a (partly) program freeze.<\/p>\n<p><u>Example<\/u><br \/>\n<i>Code<\/i><\/p>\n<pre class=\"brush: cpp; title: main.cpp; notranslate\" title=\"main.cpp\">\r\n#include &lt;thread&gt;\r\n#include &lt;mutex&gt;\r\n#include &lt;chrono&gt;\r\n#include &lt;iostream&gt;\r\n \r\nclass FooBar\r\n{\r\npublic:\r\n  void foo()\r\n  {\r\n    std::lock_guard&lt;std::mutex&gt; lock1(m_Mutex1);\r\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\r\n    std::lock_guard&lt;std::mutex&gt; lock2(m_Mutex2);\r\n    std::cout &lt;&lt; &quot;foo&quot; &lt;&lt; std::endl;\r\n  }\r\n \r\n  void bar()\r\n  {\r\n    std::lock_guard&lt;std::mutex&gt; lock1(m_Mutex2);\r\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\r\n    std::lock_guard&lt;std::mutex&gt; lock2(m_Mutex1);\r\n    std::cout &lt;&lt; &quot;bar&quot; &lt;&lt; std::endl;\r\n  }\r\nprivate:\r\n  std::mutex m_Mutex1;\r\n  std::mutex m_Mutex2;\r\n};\r\n \r\nint main()\r\n{\r\n  FooBar fb;\r\n  std::thread t1(&amp;FooBar::foo, &amp;fb), t2(&amp;FooBar::bar, &amp;fb);\r\n  t1.join(); t2.join();\r\n}\r\n<\/pre>\n<p><i>Analysis<\/i><\/p>\n<pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\r\n$ g++ main.cpp -pthread -g\r\n$ gdb a.out\r\n(gdb) run # afterwards press &#039;ctrl&#039; + &#039;c&#039;\r\n[New Thread 0x7ffff6f47700 (LWP 18927)]\r\n[New Thread 0x7ffff6746700 (LWP 18928)]\r\n^C\r\nThread 1 &quot;a.out&quot; received signal SIGINT, Interrupt.\r\n0x00007ffff762398d in pthread_join (threadid=140737336604416, thread_return=0x0) at pthread_join.c:90\r\n90\tpthread_join.c: No such file or directory.\r\n(gdb) thread apply all backtrace\r\n\r\nThread 3 (Thread 0x7ffff6746700 (LWP 18928)):\r\n#0  __lll_lock_wait () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/lowlevellock.S:135\r\n#1  0x00007ffff7624dbd in __GI___pthread_mutex_lock (mutex=0x7fffffffdc70) at ..\/nptl\/pthread_mutex_lock.c:80\r\n#2  0x0000000000400ff9 in __gthread_mutex_lock (__mutex=0x7fffffffdc70) at \/usr\/include\/x86_64-linux-gnu\/c++\/6\/bits\/gthr-default.h:748\r\n#3  0x00000000004012e2 in std::mutex::lock (this=0x7fffffffdc70) at \/usr\/include\/c++\/6\/bits\/std_mutex.h:103\r\n#4  0x0000000000401532 in std::lock_guard&lt;std::mutex&gt;::lock_guard (this=0x7ffff6745db0, __m=...) at \/usr\/include\/c++\/6\/bits\/std_mutex.h:162\r\n#5  0x000000000040148a in FooBar::bar (this=0x7fffffffdc70) at main.cpp:21\r\n#6  0x00000000004024a7 in std::__invoke_impl&lt;void, void (FooBar::* const&amp;)(), FooBar*&gt;(std::__invoke_memfun_deref, void (FooBar::* const&amp;)(), FooBar*&amp;&amp;) (__f=@0x616d90: (void (FooBar::*)(FooBar * const)) 0x40141e &lt;FooBar::bar()&gt;, \r\n    __t=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x6131&gt;) at \/usr\/include\/c++\/6\/functional:227\r\n#7  0x0000000000402420 in std::__invoke&lt;void (FooBar::* const&amp;)(), FooBar*&gt;(void (FooBar::* const&amp;)(), FooBar*&amp;&amp;) (__fn=@0x616d90: (void (FooBar::*)(FooBar * const)) 0x40141e &lt;FooBar::bar()&gt;, \r\n    __args#0=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x6131&gt;) at \/usr\/include\/c++\/6\/functional:251\r\n#8  0x00000000004023c2 in std::_Mem_fn_base&lt;void (FooBar::*)(), true&gt;::operator()&lt;FooBar*&gt;(FooBar*&amp;&amp;) const (this=0x616d90, __args#0=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x6131&gt;) at \/usr\/include\/c++\/6\/functional:604\r\n#9  0x000000000040238d in std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt;::_M_invoke&lt;0ul&gt;(std::_Index_tuple&lt;0ul&gt;) (this=0x616d88) at \/usr\/include\/c++\/6\/functional:1391\r\n#10 0x00000000004022c8 in std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt;::operator()() (this=0x616d88) at \/usr\/include\/c++\/6\/functional:1380\r\n#11 0x0000000000402298 in std::thread::_State_impl&lt;std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt; &gt;::_M_run() (this=0x616d80) at \/usr\/include\/c++\/6\/thread:197\r\n#12 0x00007ffff7b0b16f in ?? () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n#13 0x00007ffff76226ba in start_thread (arg=0x7ffff6746700) at pthread_create.c:333\r\n#14 0x00007ffff735841d in clone () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/clone.S:109\r\n\r\nThread 2 (Thread 0x7ffff6f47700 (LWP 18927)):\r\n#0  __lll_lock_wait () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/lowlevellock.S:135\r\n#1  0x00007ffff7624dbd in __GI___pthread_mutex_lock (mutex=0x7fffffffdc98) at ..\/nptl\/pthread_mutex_lock.c:80\r\n#2  0x0000000000400ff9 in __gthread_mutex_lock (__mutex=0x7fffffffdc98) at \/usr\/include\/x86_64-linux-gnu\/c++\/6\/bits\/gthr-default.h:748\r\n#3  0x00000000004012e2 in std::mutex::lock (this=0x7fffffffdc98) at \/usr\/include\/c++\/6\/bits\/std_mutex.h:103\r\n#4  0x0000000000401532 in std::lock_guard&lt;std::mutex&gt;::lock_guard (this=0x7ffff6f46db0, __m=...) at \/usr\/include\/c++\/6\/bits\/std_mutex.h:162\r\n#5  0x00000000004013a0 in FooBar::foo (this=0x7fffffffdc70) at main.cpp:13\r\n#6  0x00000000004024a7 in std::__invoke_impl&lt;void, void (FooBar::* const&amp;)(), FooBar*&gt;(std::__invoke_memfun_deref, void (FooBar::* const&amp;)(), FooBar*&amp;&amp;) (__f=@0x616c30: (void (FooBar::*)(FooBar * const)) 0x401334 &lt;FooBar::foo()&gt;, \r\n    __t=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x6131&gt;) at \/usr\/include\/c++\/6\/functional:227\r\n#7  0x0000000000402420 in std::__invoke&lt;void (FooBar::* const&amp;)(), FooBar*&gt;(void (FooBar::* const&amp;)(), FooBar*&amp;&amp;) (__fn=@0x616c30: (void (FooBar::*)(FooBar * const)) 0x401334 &lt;FooBar::foo()&gt;, \r\n    __args#0=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x6131&gt;) at \/usr\/include\/c++\/6\/functional:251\r\n#8  0x00000000004023c2 in std::_Mem_fn_base&lt;void (FooBar::*)(), true&gt;::operator()&lt;FooBar*&gt;(FooBar*&amp;&amp;) const (this=0x616c30, __args#0=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x6131&gt;) at \/usr\/include\/c++\/6\/functional:604\r\n#9  0x000000000040238d in std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt;::_M_invoke&lt;0ul&gt;(std::_Index_tuple&lt;0ul&gt;) (this=0x616c28) at \/usr\/include\/c++\/6\/functional:1391\r\n#10 0x00000000004022c8 in std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt;::operator()() (this=0x616c28) at \/usr\/include\/c++\/6\/functional:1380\r\n#11 0x0000000000402298 in std::thread::_State_impl&lt;std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt; &gt;::_M_run() (this=0x616c20) at \/usr\/include\/c++\/6\/thread:197\r\n#12 0x00007ffff7b0b16f in ?? () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n#13 0x00007ffff76226ba in start_thread (arg=0x7ffff6f47700) at pthread_create.c:333\r\n#14 0x00007ffff735841d in clone () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/clone.S:109\r\n\r\nThread 1 (Thread 0x7ffff7fdb740 (LWP 18923)):\r\n#0  0x00007ffff762398d in pthread_join (threadid=140737336604416, thread_return=0x0) at pthread_join.c:90\r\n#1  0x00007ffff7b0b3f3 in std::thread::join() () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n#2  0x00000000004010df in main () at main.cpp:33\r\n(gdb) thread 3\r\n[Switching to thread 3 (Thread 0x7ffff6746700 (LWP 18928))]\r\n#0  __lll_lock_wait () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/lowlevellock.S:135\r\n135\t..\/sysdeps\/unix\/sysv\/linux\/x86_64\/lowlevellock.S: No such file or directory.\r\n(gdb) frame 1\r\n#1  0x00007ffff7624dbd in __GI___pthread_mutex_lock (mutex=0x7fffffffdc70) at ..\/nptl\/pthread_mutex_lock.c:80\r\n80\t..\/nptl\/pthread_mutex_lock.c: No such file or directory.\r\n(gdb) p mutex.__data.__owner\r\n$1 = 18927\r\n(gdb) thread 2\r\n[Switching to thread 2 (Thread 0x7ffff6f47700 (LWP 18927))]\r\n#0  __lll_lock_wait () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/lowlevellock.S:135\r\n135\t..\/sysdeps\/unix\/sysv\/linux\/x86_64\/lowlevellock.S: No such file or directory.\r\n(gdb) frame 1\r\n#1  0x00007ffff7624dbd in __GI___pthread_mutex_lock (mutex=0x7fffffffdc98) at ..\/nptl\/pthread_mutex_lock.c:80\r\n80\t..\/nptl\/pthread_mutex_lock.c: No such file or directory.\r\n(gdb) p mutex.__data.__owner\r\n$2 = 18928\r\n(gdb) quit\r\n<\/pre>\n<p>Thread 3 (LWP 18928) waits for the mutex owned by LWP 18927<br \/>\nThread 2 (LWP 18927) waits for the mutex owned by LWP 18928<br \/>\n&#8211;> Deadlock<\/p>\n<p><i>Fix<\/i><br \/>\nRestructure the code or modify the scope of the different mutexes.<\/p>\n<p><u>Information<\/u>: There are a few awesome deadlock detection plugins for the GDB<br \/>\n&#8211; <a href=\"https:\/\/github.com\/DamZiobro\/gdb-automatic-deadlock-detector\">https:\/\/github.com\/DamZiobro\/gdb-automatic-deadlock-detector<\/a><br \/>\n&#8211; <a href=\"https:\/\/github.com\/gkoszegi\/gdb_deadlock_plugin\">https:\/\/github.com\/gkoszegi\/gdb_deadlock_plugin<\/a><\/p>\n<hr>\n<p><strong>Race condition<\/strong><br \/>\nA race condition is happening if more then one thread are executing interdependent code in a undeterministic order. The user will recognise this if the program (sometimes) behave in a (totally) different way.<\/p>\n<p><u>Example<\/u><br \/>\n<i>Code<\/i><\/p>\n<pre class=\"brush: cpp; title: main.cpp; notranslate\" title=\"main.cpp\">\r\n#include &lt;thread&gt;\r\n#include &lt;chrono&gt;\r\n#include &lt;vector&gt;\r\n#include &lt;iostream&gt;\r\n \r\nclass FooBar\r\n{\r\npublic:\r\n  FooBar() : m_Vector({1,2,3}) {}\r\n\r\n  void foo()\r\n  {\r\n    const int vecSize = m_Vector.size();\r\n    std::this_thread::sleep_for(std::chrono::milliseconds(200));\r\n    const int lastVal = m_Vector.at(vecSize-1);\r\n    std::cout &lt;&lt; &quot;lastVal: &quot; &lt;&lt; lastVal &lt;&lt; std::endl;\r\n  }\r\n \r\n  void bar()\r\n  {\r\n    std::this_thread::sleep_for(std::chrono::milliseconds(100));\r\n    m_Vector.clear();\r\n  }\r\nprivate:\r\n  std::vector&lt;int&gt; m_Vector;\r\n};\r\n \r\nint main()\r\n{\r\n  FooBar fb;\r\n  std::thread t1(&amp;FooBar::foo, &amp;fb), t2(&amp;FooBar::bar, &amp;fb);\r\n  t1.join(); t2.join();\r\n}\r\n<\/pre>\n<p><i>Analysis<\/i><\/p>\n<pre class=\"brush: plain; gutter: false; title: ; notranslate\" title=\"\">\r\n$ g++ main.cpp -pthread -g\r\n$ gdb a.out\r\n(gdb) run\r\n[New Thread 0x7ffff6f47700 (LWP 19120)]\r\n[New Thread 0x7ffff6746700 (LWP 19121)]\r\n[Thread 0x7ffff6746700 (LWP 19121) exited]\r\nterminate called after throwing an instance of &#039;std::out_of_range&#039;\r\n  what():  vector::_M_range_check: __n (which is 2) &gt;= this-&gt;size() (which is 0)\r\n\r\nThread 2 &quot;a.out&quot; received signal SIGABRT, Aborted.\r\n[Switching to Thread 0x7ffff6f47700 (LWP 19120)]\r\n0x00007ffff7286428 in __GI_raise (sig=sig@entry=6) at ..\/sysdeps\/unix\/sysv\/linux\/raise.c:54\r\n54\t..\/sysdeps\/unix\/sysv\/linux\/raise.c: No such file or directory.\r\n(gdb) catch throw\r\nCatchpoint 1 (throw)\r\n(gdb) r\r\n[New Thread 0x7ffff6f47700 (LWP 19334)]\r\n[New Thread 0x7ffff6746700 (LWP 19335)]\r\n[Thread 0x7ffff6746700 (LWP 19335) exited]\r\n[Switching to Thread 0x7ffff6f47700 (LWP 19334)]\r\n\r\nThread 2 &quot;a.out&quot; hit Catchpoint 1 (exception thrown), 0x00007ffff7adef1d in __cxa_throw () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n(gdb) bt\r\n#0  0x00007ffff7adef1d in __cxa_throw () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n#1  0x00007ffff7b08ad7 in std::__throw_out_of_range_fmt(char const*, ...) () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n#2  0x0000000000401dcc in std::vector&lt;int, std::allocator&lt;int&gt; &gt;::_M_range_check (this=0x7fffffffdcb0, __n=2) at \/usr\/include\/c++\/6\/bits\/stl_vector.h:804\r\n#3  0x0000000000401adb in std::vector&lt;int, std::allocator&lt;int&gt; &gt;::at (this=0x7fffffffdcb0, __n=2) at \/usr\/include\/c++\/6\/bits\/stl_vector.h:825\r\n#4  0x00000000004013c8 in FooBar::foo (this=0x7fffffffdcb0) at main.cpp:15\r\n#5  0x0000000000402c95 in std::__invoke_impl&lt;void, void (FooBar::* const&amp;)(), FooBar*&gt;(std::__invoke_memfun_deref, void (FooBar::* const&amp;)(), FooBar*&amp;&amp;) (__f=@0x617c50: (void (FooBar::*)(FooBar * const)) 0x401360 &lt;FooBar::foo()&gt;, \r\n    __t=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x732b&gt;) at \/usr\/include\/c++\/6\/functional:227\r\n#6  0x0000000000402c0e in std::__invoke&lt;void (FooBar::* const&amp;)(), FooBar*&gt;(void (FooBar::* const&amp;)(), FooBar*&amp;&amp;) (__fn=@0x617c50: (void (FooBar::*)(FooBar * const)) 0x401360 &lt;FooBar::foo()&gt;, \r\n    __args#0=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x732b&gt;) at \/usr\/include\/c++\/6\/functional:251\r\n#7  0x0000000000402bb0 in std::_Mem_fn_base&lt;void (FooBar::*)(), true&gt;::operator()&lt;FooBar*&gt;(FooBar*&amp;&amp;) const (this=0x617c50, __args#0=&lt;unknown type in \/home\/user\/Documents\/gdb-tests\/a.out, CU 0x0, DIE 0x732b&gt;) at \/usr\/include\/c++\/6\/functional:604\r\n#8  0x0000000000402b7b in std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt;::_M_invoke&lt;0ul&gt;(std::_Index_tuple&lt;0ul&gt;) (this=0x617c48) at \/usr\/include\/c++\/6\/functional:1391\r\n#9  0x0000000000402ab6 in std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt;::operator()() (this=0x617c48) at \/usr\/include\/c++\/6\/functional:1380\r\n#10 0x0000000000402a86 in std::thread::_State_impl&lt;std::_Bind_simple&lt;std::_Mem_fn&lt;void (FooBar::*)()&gt; (FooBar*)&gt; &gt;::_M_run() (this=0x617c40) at \/usr\/include\/c++\/6\/thread:197\r\n#11 0x00007ffff7b0b16f in ?? () from \/usr\/lib\/x86_64-linux-gnu\/libstdc++.so.6\r\n#12 0x00007ffff76226ba in start_thread (arg=0x7ffff6f47700) at pthread_create.c:333\r\n#13 0x00007ffff735841d in clone () at ..\/sysdeps\/unix\/sysv\/linux\/x86_64\/clone.S:109\r\n(gdb) f 4\r\n#4  0x00000000004013c8 in FooBar::foo (this=0x7fffffffdcb0) at main.cpp:15\r\n15\t    const int lastVal = m_Vector.at(vecSize-1);\r\n(gdb) info locals\r\nvecSize = 3\r\nlastVal = 0\r\n(gdb) p m_Vector\r\n$1 = std::vector of length 0, capacity 3\r\n(gdb) quit\r\n<\/pre>\n<p>Someone has already cleared m_Vector<br \/>\n&#8211;> Race condition<\/p>\n<p><i>Fix<\/i><br \/>\nRestructure the code or use a mutex to synchronize the access on that member variable.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The two most common problems that are happing when dealing with multithreading are the deadlock and the race condition. Follow<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false},"categories":[28],"tags":[],"_links":{"self":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/3444"}],"collection":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=3444"}],"version-history":[{"count":1,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/3444\/revisions"}],"predecessor-version":[{"id":16732,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=\/wp\/v2\/posts\/3444\/revisions\/16732"}],"wp:attachment":[{"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=3444"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=3444"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.max-sperling.bplaced.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=3444"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}