locked
What are the best practices for synchronization? RRS feed

  • Question

  • Hi,

    I'm trying to find out what the best practices for synchronization are. As far as I know I could use:

    1. Task.Wait, Task.WaitAll, Task.WaitAny to wait on one ore more tasks
    2. Continuation tasks to chain tasks
    3. TaskFactory.ContinueWhenAny, TaskFactory.ContinueWhenAll to create a continuation that runs if all or any of multiple tasks complete
    4. The threadsafe collections in System.Collections.Concurrency (like BlockingCollection<T>) if I want to share list data between tasks
    5. The methods of the Interlocked class to write to integers and doubles threadsafe
    6. A lock (lock keyword or Monitor.Enter, Monitor.Leave) to create critical sections (to threadsafe access shared data)
    7. A Mutex to synchronize over processes
    8. Wait Handles (ManualResetEventSlim, AutoResetEvent) to wait on a signal set by another thread
    9. SemaphoreSlim to "limit the number of threads that can access a resource or pool of resources concurrently"
    10. Barrier to enable "multiple tasks to cooperatively work on an algorithm in parallel through multiple phases"
    11. ReaderWriterLockSlim to allow multiple threads to read shared data but only on to write at the same time
    12. CountDownEvent to define a certain number of signaling that must occur before the event signals
    13. An AsyncOperation instance to post messages from one thread to the other
    14. The Invoke method to run delegates in the UI thread
    15. A continuation that used the UI TaskScheduler (http://andyclymer.blogspot.com/2006/10/synchronizationcontext-assists.html) to run the cntinuation on the UI thread  
    Is that the best practices for the different cases? Did I forget something that is not "old style" like Thread.Join? Could I use the SynchronizationContext as well to synchronize? Where soes the Concurrency and Coordination Runtime come in when synchronization is a major issue?

     

    Wednesday, April 28, 2010 12:46 PM

Answers

  • Hi Juergen-

    I'd suggest reading the document at http://download.microsoft.com/download/B/C/F/BCFD4868-1354-45E3-B71B-B851CD78733D/ParallelProgramsinNET4_CodingGuidelines.pdf. It provides high-level guidance for this kind of thing.  It's difficult to come up with more-detailed specific best practices, as the best practice/guidance often depends on circumstance and details of the code/situation/pattern in question. 

    Most of the items on your list are fine, and many are just restating the purpose for each of the types to exist.  A few specific comments:

    #5. I would not recommend using the Interlocked class as a best practice... lock-free code is difficult to get right, so whenever possible I recommend using higher-level primitives (e.g. Monitor) until you've proved that your performance is really suffering as a result and lock-freedom is the only way to get the performance you need.

    #11.  Even for situations where there are readers and writers, ReaderWriterLockSlim isn't always better than Monitor.  Tracking information about readers and writers is more costly than just tracking mutual exclusion, so for small critical regions being protected, Monitor is often less overhead than ReaderWriterLockSlim and may result in better performance.

    #13. AsyncOperation is really about posting back to a captured SynchronizationContext, which is almost always about getting back to the UI thread.  This is a subset of posting from one thread to another, which is a general producer/consumer pattern.  AsyncOperation may be the right solution for some problems, but it's not a general producer/consumer solution.

    #14. You mean Control/Dispatcher.Invoke for Windows Forms/WPF, right (as opposed to delegate.Invoke)?  Often BeginInvoke is a better solution, or using AsyncOperation or TaskScheduler.FromCurrentSynchronizationContext as you've mentioned in #13 and #15, as they both abstract away the UI framework being utilized, whereas accessing Control.{Begin}Invoke or Dispatcher.{Begin}Invoke ties your code to a specific UI framework.

    Thursday, April 29, 2010 9:43 PM
    Moderator

All replies

  • Hi Juergen-

    I'd suggest reading the document at http://download.microsoft.com/download/B/C/F/BCFD4868-1354-45E3-B71B-B851CD78733D/ParallelProgramsinNET4_CodingGuidelines.pdf. It provides high-level guidance for this kind of thing.  It's difficult to come up with more-detailed specific best practices, as the best practice/guidance often depends on circumstance and details of the code/situation/pattern in question. 

    Most of the items on your list are fine, and many are just restating the purpose for each of the types to exist.  A few specific comments:

    #5. I would not recommend using the Interlocked class as a best practice... lock-free code is difficult to get right, so whenever possible I recommend using higher-level primitives (e.g. Monitor) until you've proved that your performance is really suffering as a result and lock-freedom is the only way to get the performance you need.

    #11.  Even for situations where there are readers and writers, ReaderWriterLockSlim isn't always better than Monitor.  Tracking information about readers and writers is more costly than just tracking mutual exclusion, so for small critical regions being protected, Monitor is often less overhead than ReaderWriterLockSlim and may result in better performance.

    #13. AsyncOperation is really about posting back to a captured SynchronizationContext, which is almost always about getting back to the UI thread.  This is a subset of posting from one thread to another, which is a general producer/consumer pattern.  AsyncOperation may be the right solution for some problems, but it's not a general producer/consumer solution.

    #14. You mean Control/Dispatcher.Invoke for Windows Forms/WPF, right (as opposed to delegate.Invoke)?  Often BeginInvoke is a better solution, or using AsyncOperation or TaskScheduler.FromCurrentSynchronizationContext as you've mentioned in #13 and #15, as they both abstract away the UI framework being utilized, whereas accessing Control.{Begin}Invoke or Dispatcher.{Begin}Invoke ties your code to a specific UI framework.

    Thursday, April 29, 2010 9:43 PM
    Moderator
  • Thanks very much, Stephen.

    Again a very detailed clarification :-)
    Friday, April 30, 2010 7:56 PM