Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>In a multithreading environment you have to take care of <strong>synchronization</strong> so two threads doesn't clobber the state by simultaneously performing modifications. Otherwise you can have race conditions in your code (for an example see the <a href="http://en.wikipedia.org/wiki/Therac-25" rel="noreferrer">infamous Therac-25 accident</a>.) You also have to <strong>schedule</strong> the threads to perform various tasks. You then have to make sure that your synchronization and scheduling doesn't cause a <strong>deadlock</strong> where multiple threads will wait for each other indefinitely.</p> <p><strong>Synchronization</strong></p> <p>Something as simple as increasing a counter requires synchronization:</p> <pre><code>counter += 1; </code></pre> <p>Assume this sequence of events:</p> <ul> <li><code>counter</code> is initialized to 0</li> <li>thread A retrieves <code>counter</code> from memory to cpu (0)</li> <li><strong>context switch</strong></li> <li>thread B retrieves <code>counter</code> from memory to cpu (0)</li> <li>thread B increases <code>counter</code> on cpu</li> <li>thread B writes back <code>counter</code> from cpu to memory (1)</li> <li><strong>context switch</strong></li> <li>thread A increases <code>counter</code> on cpu</li> <li>thread A writes back <code>counter</code> from cpu to memory (1)</li> </ul> <p>At this point the <code>counter</code> is 1, but both threads did try to increase it. Access to the counter has to be synchronized by some kind of locking mechanism:</p> <pre><code>lock (myLock) { counter += 1; } </code></pre> <p>Only one thread is allowed to execute the code inside the locked block. Two threads executing this code might result in this sequence of events:</p> <ul> <li>counter is initialized to 0</li> <li>thread A acquires <code>myLock</code></li> <li><strong>context switch</strong></li> <li>thread B tries to acquire <code>myLock</code> but has to wait</li> <li><strong>context switch</strong></li> <li>thread A retrieves <code>counter</code> from memory to cpu (0)</li> <li>thread A increases <code>counter</code> on cpu</li> <li>thread A writes back <code>counter</code> from cpu to memory (1)</li> <li>thread A releases <code>myLock</code></li> <li><strong>context switch</strong></li> <li>thread B acquires <code>myLock</code></li> <li>thread B retrieves <code>counter</code> from memory to cpu (1)</li> <li>thread B increases <code>counter</code> on cpu</li> <li>thread B writes back <code>counter</code> from cpu to memory (2)</li> <li>thread B releases <code>myLock</code></li> </ul> <p>At this point <code>counter</code> is 2.</p> <p><strong>Scheduling</strong></p> <p>Scheduling is another form of synchronization and you have to you use thread synchronization mechanisms like events, semaphores, message passing etc. to start and stop threads. Here is a simplified example in C#:</p> <pre><code>AutoResetEvent taskEvent = new AutoResetEvent(false); Task task; // Called by the main thread. public void StartTask(Task task) { this.task = task; // Signal the worker thread to perform the task. this.taskEvent.Set(); // Return and let the task execute on another thread. } // Called by the worker thread. void ThreadProc() { while (true) { // Wait for the event to become signaled. this.taskEvent.WaitOne(); // Perform the task. } } </code></pre> <p>You will notice that access to <code>this.task</code> probably isn't synchronized correctly, that the worker thread isn't able to return results back to the main thread, and that there is no way to signal the worker thread to terminate. All this can be corrected in a more elaborate example.</p> <p><strong>Deadlock</strong></p> <p>A common example of deadlock is when you have two locks and you are not careful how you acquire them. At one point you acquire <code>lock1</code> before <code>lock2</code>:</p> <pre><code>public void f() { lock (lock1) { lock (lock2) { // Do something } } } </code></pre> <p>At another point you acquire <code>lock2</code> before <code>lock1</code>:</p> <pre><code>public void g() { lock (lock2) { lock (lock1) { // Do something else } } } </code></pre> <p>Let's see how this might deadlock:</p> <ul> <li>thread A calls <code>f</code></li> <li>thread A acquires <code>lock1</code></li> <li><strong>context switch</strong></li> <li>thread B calls <code>g</code></li> <li>thread B acquires <code>lock2</code></li> <li>thread B tries to acquire <code>lock1</code> but has to wait</li> <li><strong>context switch</strong></li> <li>thread A tries to acquire <code>lock2</code> but has to wait</li> <li><strong>context switch</strong></li> </ul> <p>At this point thread A and B are waiting for each other and are deadlocked.</p>
 

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