Interlocked class provides a set of static functions which allow a few simple operations to be performed thread safe. Apart from convenience, these functions are much faster than having a lock around an equivalent statement (explained below). First, following is a list of Interlocked functions with explanation in comments.
long counter = 0; //Increment counter by 1 Interlocked.Increment (ref counter); //Decrement counter by 1 Interlocked.Decrement (ref counter); //Add 10 to counter Interlocked.Add (ref counter, 10); //Set counter = 100 and return the original value of counter before it was changed. //This usually does not require a lock but consider a scenario where //counter is a 64 bit value and code running on a 32 bit machine. //In such a scenario the assignment happens in 2 steps for each 32 bit part //and hence the assignment is not atomic. var oldCounterValue = Interlocked.Exchange (ref counter, 100); // Read a 64-bit value atomically into another variable (val) var val = Interlocked.Read(ref counter); // Set counter = 999 if counter == 1000 Interlocked.CompareExchange(ref counter, 999, 1000);
As I mentioned these statements are faster in most cases and especially on modern CPUs, as these functions execute in a lock free manner or as a single instruction wherever possible, thus avoiding the need for a lock (they do use memory barriers though, to avoid reordering issues etc.) For example CompareExchange function uses the cpu instruction CMPXCHG which allows Comparison and Exchange of values in a single instruction which cannot be pre-empted.
In Desktop Applications (WPF or Winforms), any UI element created on a thread is accessible only to that thread, and is not available to other threads. This is called Thread Affinity. If an attempt is made to access the ui element from any thread, other than a thread that created it, it can leave the state of the control in an invalid state or the runtime will throw an exception. In WPF all objects which subsequently derive from DispatcherObject have threads affinity (Yes, that includes some non UI objects like ObservableCollection too). In winforms objects deriving from Control class have thread affinity. So what if some thread wants to access a UI element not created by it? As it cannot run the code which accesses the UI element, it has to marshall the code to its owner, so that the owner thread can run the code. We have a few options to simplify this task:
Using Dispatcher.Invoke / BeginInvoke (WPF)
Action uiAction = () => txtbox.Text = "Done"; // The code we want to pass to UI thread Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, uiAction);
Using Control.Invoke / BeginInvoke (Winforms)
Action uiAction = () => txtbox.Text = "Done"; txtbox.Invoke(uiAction);
Using BackgroundWorker
Instead of manually assigning work to another thread from UI thread, you can use BackgroundWorker class.
var bgWorker = new BackgroundWorker { WorkerReportsProgress = true }; bgWorker.DoWork += (o, e) => { //Worker thread code. Gets called in a Non-UI thread. }; bgWorker.ProgressChanged += (o, e) => { //Progress change gets called on the UI thread. Controls can be accessed safely }; bgWorker.RunWorkerCompleted += (o, e) => { //gets called when worker thread finishes. UI thread. Controls can be accessed safely }; bgWorker.RunWorkerAsync();
Using Task Continuations
If we use the TaskScheduler returned from helper function TaskScheduler.FromCurrentSynchronizationContext() in the continuation task, the code will get executed in a UI thread.
Task.Factory.StartNew<string>(CodeToRunOnNonUIThread) .ContinueWith(ant => txtBox.Text = "Done", TaskScheduler.FromCurrentSynchronizationContext());
CountdownEvent is a synchronization primitive that unblocks its waiting threads after it has been signaled a certain number of times. E.g. if we create a CountdownEvent with a parameter of 3, threads waiting on it will remain blocked until a Signal event is called 3 times.
static CountdownEvent countdownEvent= new CountdownEvent (3); static void Main() { new Thread (Func).Start (1); new Thread (Func).Start (2); new Thread (Func).Start (3); countdownEvent.Wait(); // The thread will block until we receieve three Signals. Console.WriteLine("Three Signals received. Now I can proceed"); } static void Func (object id) { Thread.Sleep((int)id * 1000); Console.WriteLine("Signalling"); countdownEvent.Signal(); }
Notes:
CountdownEvent is available only in .net framework 4.0 and above
It is purely managed. So cannot be used for interprocess signalling.
CountdownEvent’s count can be increased by calling the AddCount function (beware if it has already reached zero, AddCount will throw an exception)
Calling Reset() will reset its count to the original value and will unsignal the construct.
ManualResetEvent and AutoResetEvents are signalling constructs. We'd use these constructs if we want threads to wait until signalled.
ManualResetEvent is like a door. If the door is closed all threads would wait at the door. Some other thread can signal for the door to open. Once opened all threads can pass through the "door" and continue execution. The door can be closed again.
Creating the ManualResetEvent object. (the "door")
ManualResetEvent mre = new ManualResetEvent(false);
false = door is closed by default, true = door is open by default.
Wait for the door to open:
mre.WaitOne();
Opening the door:
mre.Set();
Closing the door:
mre.Reset();
AutoResetEvent is similar to ManualResetEvent except that it behaves as a ticket turnstile instead of a door i.e. when signalled only one thread is allowed to proceed and the gate is automatically closed. Other threads then have to wait for more signals.
Creating a AutoResetEvent.
AutoResetEvent are = new AutoResetEvent(false);
false = turnstile is created as closed , true = turnstile is created as open.
Other methods are similar to ManualResetEvent.
Note that if no thread is waiting and a Set is called, the signal is not lost. The "turnstile" remains open until some thread calls WaitOne and is allowed to enter.
Semaphores are used to prevent too many threads from accessing a resource. A good analogy I read in Albahari's book (C# in a nutshell) is to compare a semaphore with a bouncer in a nightclub. A nightclub has a certain capacity. The bouncer allows people to enter the nightclub till it is full. Once its full a queue builds up outside the nightclub and a person is allowed to enter only when some one else leaves the nightclub.
Semaphore is like the bouncer. A semaphore is created with a fixed capacity. Only a fixed amount of threads are allowed to access a resource determined by its capacity. Once its full all other threads have to wait till some threads leaves the resource.
class NightClub { static SemaphoreSlim semaphore = new SemaphoreSlim (5); static void Main() { for (int i = 1; i <= 10; i++) new Thread (EnterTheClub).Start (i); } static void EnterTheClub (object id) { Console.WriteLine (id + " queues up"); semaphore .Wait(); Console.WriteLine (id + " is in!"); Thread.Sleep (1000 * (int) id); Console.WriteLine (id + " is leaving"); semaphore.Release(); } }
Output
4 queues up 1 queues up 1 is in! 5 queues up 7 queues up 7 is in! 5 is in! 4 is in! 8 queues up 8 is in! 9 queues up 10 queues up 3 queues up 6 queues up 2 queues up 1 is leaving 9 is in! 4 is leaving 10 is in! 5 is leaving 3 is in! 7 is leaving 6 is in! 8 is leaving 2 is in! 3 is leaving 9 is leaving 2 is leaving 6 is leaving 10 is leavingSemaphoreSlim and Semaphore are functionally similar. SemaphoreSlim is about 4 times faster than a Semaphore but SemaphoreSlim cannot be used for interprocess signalling.