The runtime provides a number of ways for users to synchronize access to instance and static methods and instance fields. This starts at the top of scale with the synchronized context property that has an easy to use construct; through synchronized code regions for more sophisticated usage; all the way down to primitives that allow users to roll their own synchronization mechanisms.
The runtime provides a thread model where classes are categorized into a number of categories that can be synchronized in a variety of different ways depending on the requirements.
The rows show the synchronization category. The columns show what synchronization support is provided for fields and methods.
Category | Global Fields | Static Fields | Static Methods | Instance Fields | Instance Methods | Specific Code blocks |
Not Synchronized | No | No | No | No | No | No |
Synchronized Context | No | No | No | Yes | Yes | No |
Synchronized Context with Thread Affinity | No | No | No | Yes | Yes | No |
Synchronized Code Region | No | No | Only Marked | No | Only Marked | On Marked |
Synchronized Manually | Manual | Manual | Manual | Manual | Manual | Manual |
Default for objects. Instance and static methods and fields are not synchronized. Any thread can access any method or field at any time.
Key Points
The synchronization primitives Interlocked, Monitor, ReaderWriterLock, Manual & Auto Reset Events can be used to manually take & release the lock for the particular primitive. They can be used to protect Global, Static and Instance fields. Likewise they can be used to protect global, static and instance methods.
Key Points
Code blocks, instance and static methods which are marked with a language keyword. Only a single thread at any one time is allowed to hold the sync block lock.
Key Points
Synchronized code regions obtain the sync block lock when entering the region or waiting until the lock has been released and releasing the lock when exiting the synchronized code region.
Some compilers support decorating blocks of code with a particular language keyword. The resulting generated code will try and take the lock when the code is executed. If the lock has already been taken then the executing code will wait until the lock becomes available. When the code exits the synchronized block of code the lock is released.
A runtime method can be marked with a metadata bit that will enforce synchronized access to the method. When the method is called the Runtime will synchronize on the SyncBlock.
On instance methods, ‘this’ is used for synchronizing i.e. the SyncBlock for the object is used for synchronization.
On static methods, the class object is used for synchronizing i.e. the SyncBlock for the class is used for synchronization.
Thread.Interrupt can be used to break a thread out of blocking operations such as waiting for access to a synchronized region of code. Thread.Interrupt is also used to break threads out of operations like Object.Wait and Thread.Sleep.
The Monitor class provides a mechanism for synchronizing on objects via the sync block. This could be used to provide synchronized access to state in an object. Multiple threads could synchronize on the Monitor Object.
The C# lock keyword uses Monitor.Enter and Monitor.Exit to lock the object.
class Foo { void Method1() { lock(this) { //. . . Do some interesting work } } }
If an exception is thrown in the “Do some interesting work” code block, the lock acquired by the lock(this) will be automatically released. The C# compiler emits a try / finally block with the Montior.Enter at the beginning of the try, and the Monitor.Exit in the finally block (therefore if an exception is thrown inside of the "lock" block, the finally handler will be run and therefore cleanup).
All instance methods and fields are synchronized. Multiple threads are allowed to access the methods and fields but only a single thread is allowed at any one time.
All objects in the same context / synchronization domain share the same lock. The type / class is attributed with the Synchronized Context Attribute Property.
All instance methods and fields are synchronized. Only a single thread is ever used to access the methods and fields.
All objects in the same context / synchronization domain share the same thread. The type / class is attributed with the Thread Affinity Context Attribute Property