Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>The technique you describe isn't just common: it's the one technique that has been proven to work all the time. There are a few other rules you should follow when coding threaded code in C++, though, among which the most important may be:</p> <ul> <li><strong>don't hold a lock when calling a virtual function</strong>: even if at the time you're writing your code you know which function will be called and what it will do, code evolves, and virtual functions are there to be overridden, so ultimately, you <em>won't</em> know what it does and whether it will take any other locks, meaning you will lose your guaranteed order of locking</li> <li><strong>watch out for race conditions</strong>: in C++, nothing will tell you when a given piece of data is shared between threads and you don't use some kind of synchronization on it. One example of this was posted in the C++ Lounge on SO chat a few days ago, by Luc, as an example of this (code at the end of this post): just trying to synchronize on something <em>else</em> that happens to be in the neighborhood doesn't mean your code is correctly synchronized.</li> <li><strong>try to hide asynchronous behavior</strong>: you're <em>usually</em> better hiding your concurrency in your software's architecture, such that most calling code won't care whether there's a thread there or not. It makes the architecture easier to work with - especially for some-one who isn't used to concurrency.</li> </ul> <p>I could go on for a while, but in my experience, the <em>easiest</em> way to work with threads is using patterns that are well-known to everyone who might work with the code, such as the producer/consumer pattern: it's easy to explain and you only need one tool (a queue) to allow your threads to communicate with each other. After all, the <em>only</em> reason for two threads to be synchronized with each other, is to allow them to communicate.</p> <p>More general advice: </p> <ul> <li>Don't try your hand at lock-free programming until you've had experience with concurrent programming using locks - it's an easy way to blow your foot off, or run into very strange bugs.</li> <li>Reduce the number of shared variables and the number of times those variables are accessed to a bare minimum.</li> <li>Don't count on two events always occurring in the same order, even if you can't see any way of them reversing order.</li> <li>More generally: don't count on timing - don't think a given task should always take a given amount of time.</li> </ul> <h3>The following code will fail:</h3> <pre><code>#include &lt;thread&gt; #include &lt;cassert&gt; #include &lt;chrono&gt; #include &lt;iostream&gt; #include &lt;mutex&gt; void nothing_could_possibly_go_wrong() { int flag = 0; std::condition_variable cond; std::mutex mutex; int done = 0; typedef std::unique_lock&lt;std::mutex&gt; lock; auto const f = [&amp;] { if(flag == 0) ++flag; lock l(mutex); ++done; cond.notify_one(); }; std::thread threads[2] = { std::thread(f), std::thread(f) }; threads[0].join(); threads[1].join(); lock l(mutex); cond.wait(l, [done] { return done == 2; }); // surely this can't fail! assert( flag == 1 ); } int main() { for(;;) nothing_could_possibly_go_wrong(); } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload