EXCLUSIVES
Writing High Performance .NET Code
Threading Tips
a) Distribute the work equally among threads – If the work is imbalanced, one thread will finish the work quickly, but must wait till other thread(s) finish their job, impacting performance.
b) Don’t use too much shared data among threads – If any data or data structure is shared among threads, then synchronization is required to update that data. This increases the amount of serial code/paths in your application hurting your scalability
c) Acquire Lock late and release it early. This is very important as you must take a lock just before you absolutely have to and release it first before doing anything once the atomic region is executed. Here is an example in .NET
void foo ()
{
int a, b;
…. //some code
//Following code has to be atomically executed
{
}
…. //Some other code
//End of atomic region
}
//WRONG: Increased atomic region. Lock will be held longer thus hurting performance
void foo ()
{
int a, b;
Object obj ; //for synchronization
Monitor.Enter(); or lock(obj) {
…. //some code
//Following code has to be atomically executed
{
}
…. //Some other code
Monitor.Exit(); or }
//End of atomic region
}
//WRONG: Entire function is synchronized. Bad idea.
using System.Runtime.CompilerServices;
MethodImplAttribute(MethodImplOptions.Synchronized)]
void foo ()
{
int a, b;
…. //some code
//Following code has to be atomically executed
{
}
…. //Some other code
//End of atomic region
}
//Correct: Synchronizing just that block which needs atomic execution
void foo ()
{
int a, b;
Object obj;
…. //some code
lock(obj) {
//Following code has to be atomically executed
{
}
}//end of lock
//End of atomic region
…. //Some other code
}
d) Use proper synchronization primitives: There are multiple synchronization primitives that are provided by .NET Framework. These vary from fewer features (very fast) to many features (very slow). It is important to use this correctly to get optimal performance. Synchoronization primitives can be defined as:
d.a. Monitor or lock: Provides a mechanism that synchronizes access to objects
d.b. Interlocked: Provides atomic access to variables that are shared by multiple threads. For example: for any atomic ++ or –- operations consider using Interlocked class
d.c. Mutex: Synchronization primitives that can be used for inter process synchronization. They are considerably slower; use it when you absolutely need it.
d.d. ReaderWriterLock: Lock that supports single writer and multiple readers. If you have a scenario where you read your data frequently but update only once in a while, consider using this as it supports multiple readers.
d.e. ReaderWriterLockSlim: Similar to ReaderWriterLock but simplified rules for recursion and for upgrading and downgrading lock state. It also avoids many cases of potential deadlock and has improved performance. Using this is recommended.
d.f. Semaphore: Limits # of threads that can access a resource or pool of resources concurrently. Use it only when you need to control pool of resources.
e) Never use Thread.Suspend and Thread.Resume to synchronize activities. The suspend and resume operation doesn’t happen immediately as CLR has to make sure the execution control is in safe point. This can lead to race conditions or deadlock (1)
f) Never use Thread.Abort to abort another thread: (1)




Please wait...