Synchronizing Data for Multithreading

When multiple threads can make calls to the properties and methods of a single object, it is critical that those calls be synchronized. Otherwise one thread might interrupt what another thread is doing, and the object could be left in an invalid state. A class whose members are protected from such interruptions is called thread-safe.

The Common Language Infrastructure provides several strategies to synchronize access to instance and static members:

  • Synchronized contexts. You can use the SynchronizationAttribute to enable simple, automatic synchronization for ContextBoundObject objects.
  • Synchronized property. A few classes, such as Hashtable and Queue, provide a Synchronized property that returns a thread-safe wrapper for an instance of the class. See Collections and Synchronization (Thread Safety).
  • Synchronized code regions. You can use the Monitor class or compiler support for this class to synchronize only the code block that needs it, improving performance.
  • Manual synchronization. You can use the various synchronization objects to create your own synchronization mechanisms.

The common language runtime provides a thread model in which classes fall into a number of categories that can be synchronized in a variety of different ways depending on the requirements. The following table shows what synchronization support is provided for fields and methods with a given synchronization category.

Category Global fields Static fields Static methods Instance fields Instance methods Specific code blocks
No Synchronization No No No No No No
Synchronized Context No No No Yes Yes No
Synchronized Code Regions No No Only if marked No Only if marked Only if marked
Manual Synchronization Manual Manual Manual Manual Manual Manual

No Synchronization

This is the default for objects. Any thread can access any method or field at any time. Only one thread should access these objects.

Synchronized Context

You can use the SynchronizationAttribute on any ContextBoundObject to synchronize all instance methods and fields. All objects in the same context domain share the same lock. Multiple threads are allowed to access the methods and fields, but only a single thread is allowed at any one time.

Synchronized Code Regions

You can use the Monitor class or a compiler keyword to synchronize blocks of code, instance methods, and static methods. There is no support for synchronized static fields.

Both Visual Basic and C# support the marking of blocks of code with a particular language keyword. The resulting generated code attempts to take the lock when the code is executed. If the lock has already been taken, the executing code waits until the lock becomes available. When the code exits the synchronized block of code, the lock is released. You can also decorate a method with a MethodImplAttribute and by passing MethodImplOptions.Synchronized, which results in essentially the same thing as using Monitor or one of the compiler keywords for a Monitor code block.

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 Thread.Sleep.

The Monitor class supports the following code-block synchronization:

  • Synchronized instance methods. On instance methods, the current object (the this keyword in C# or the Me keyword in Visual Basic) is used for synchronization.
  • Synchronized static methods. On static methods, the class is used for synchronization.

Compiler Support

Both Visual Basic and C# support a language keyword that uses Monitor.Enter and Monitor.Exit to lock the object. Visual Basic supports the SyncLock statement; C# supports the lock statement.

In both cases, if an exception is thrown in the code block, the lock acquired by the lock or SyncLock is released automatically. The C# and Visual Basic compilers emit a try/finally block with Monitor.Enter at the beginning of the try, and Monitor.Exit in the finally block. If an exception is thrown inside the lock or SyncLock block, the finally handler runs to allow you to do any clean-up work.

Manual Synchronization

You can use the synchronization classes Interlocked, Monitor, ReaderWriterLock, ManualResetEvent, and AutoResetEvent to take and release the lock to protect global, static, and instance fields and global, static, and instance methods. For more information, see Threading Objects and Features.

See Also

Threads and Threading | Monitor | Interlocked | WaitHandle | ManualResetEvent | AutoResetEvent | ReaderWriterLock | SyncLock | lock | SynchronizationAttribute