
C# ПІДРУЧНИКИ / c# / MS Press - Msdn Training Programming Net Framework With C#
.pdf

Module 14 (Optional): Threading and Asynchronous Programming |
35 |
|
|
|
|
Note There are two classes named SynchronizationAttribute: one in the System.EnterpriseServices namespace, and the other in the
System.Runtime.Remoting.Contexts namespace. The System.EnterpriseServices.SynchronizationAttribute class supports only synchronous calls, and can be used only with serviced components. (For more information about serviced components, see Serviced Component Overview in the .NET Framework SDK documentation.) The
System.Runtime.Remoting.Contexts.SynchronizationAttribute supports both synchronous and asynchronous calls, and can be used only with context bound objects. This topic discusses only the
System.Runtime.Remoting.Contexts.SynchronizationAttribute.


Module 14 (Optional): Threading and Asynchronous Programming |
37 |
|
|
|
|
[MethodImplAttribute(MethodImplOptions.Synchronized)]
In the following example, the class CounterSynchronizedCodeRegion has both its instance method IncrementInstance and static method IncrementStatic attributed for synchronized access. This allows only one thread to execute either method at one time.
using System;
using System.Threading;
using System.Runtime.CompilerServices;
class ASynchronizedClassExample
{
static int staticValue = 0; int instanceValue = 0;
[MethodImplAttribute(MethodImplOptions.Synchronized)] public void ChangeInstance()
{
// thread safe changes to instanceValue
}
[MethodImplAttribute(MethodImplOptions.Synchronized)] public static void ChangeStatic()
{
// thread safe changes to staticValue
}
}
//…
Note You can use Thread.Interrupt to break a thread out of blocking operations, such as waiting for access to a synchronized region of code.
Monitor Class and the C# Compiler lock Keyword
Using the Monitor class or Visual C# lock keyword is a little more complex than using MethodImplAttribute, but these approaches give you the flexibility to synchronize access to a specific region of code and not just to the entire method. By synchronizing the least amount of code required for thread safety, you maximize the amount of possible concurrency.
The Monitor class supports the following code-block synchronization:
!Synchronized instance methods
On instance methods, the current object is used for synchronization.
Visual C# uses the this keyword to indicate the current object, while Visual Basic .NET use the Me keyword.
!Synchronized static methods
On static methods, the class is used for synchronization.
The Monitor class provides methods that you can use to synchronize access to a specific region of code by taking and releasing a lock on a particular object. After you obtain a lock on a code region, you can use the Monitor.Wait,
Monitor.Pulse, and Monitor.PulseAll methods.

38 |
Module 14 (Optional): Threading and Asynchronous Programming |
Monitor.Wait
The Monitor.Wait method releases the lock on an object and blocks the current thread until it reacquires the lock. The thread that currently owns the lock on the specified object invokes this method to release the object so that another thread can access it. The caller is blocked while waiting to reacquire the lock. This method is called when the calling thread is waiting for a change in the state of the object that will occur as a result of another thread’s operations on the object.
When a thread calls Wait, it releases the lock on the object and enters the object’s waiting queue. The next thread in the object’s ready queue, if there is one, acquires the lock and has exclusive use of the object. All threads that call Wait remain in the waiting queue until they receive a signal from Pulse or PulseAll, sent by the owner of the lock. If Pulse is sent, only the thread at the head of the waiting queue is affected. If PulseAll is sent, all threads that are waiting for the object are affected. When the signal is received, one or more threads leave the waiting queue and enter the ready queue. A thread in the ready queue is permitted to reacquire the lock.
This method returns when the calling thread reacquires the lock on the object.
Note that this method blocks indefinitely if the holder of the lock does not call
Pulse or PulseAll.
Caution You should check whether the Pulse method is ever called before its corresponding Wait method is called. A thread must have already issued a Wait for the Pulse to affect it. You can use the Monitor.Wait methods with a timeout parameter to prevent the caller of the Wait from blocking indefinitely when such a condition occurs.
Monitor.Pulse
The Monitor.Pulse method notifies a thread in the waiting queue of a change in the locked object's state. Only the current owner of the lock can signal a waiting object by using Pulse. The thread that currently owns the lock on the specified object invokes this method to signal the next thread in line for the lock. Upon receiving the pulse, the waiting thread is moved to the ready queue. When the thread that invoked Pulse releases the lock, the next thread in the ready queue, which is not necessarily the thread that was pulsed, acquires the lock.
Note Be aware of the distinction between the use of Monitor and WaitHandle objects. Monitor objects are purely managed, fully portable, and may be more efficient in terms of operating system resource requirements. WaitHandle objects represent operating system waitable objects, are useful for synchronizing between managed and unmanaged code, and expose some advanced operating system features like the ability to wait on many objects at once.
Module 14 (Optional): Threading and Asynchronous Programming |
39 |
|
|
|
|
Compiler Support
Both Visual Basic and Visual C# support a language keyword that uses
Monitor.Enter and Monitor.Exit to lock the object. Visual Basic supports the
SyncLock keyword; Visual C# supports the lock keyword.
When you use the lock or SyncLock keyword, the compiler generates code. The Visual 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 of the lock or SyncLock block, the finally handler runs to allow you to perform any clean-up work.
The C# statement, of the form lock(x) where x is an expression of a referencetype, is equivalent to the following except that x is only evaluated once:
System.Threading.Monitor.Enter(x); try {
...
}
finally { System.Threading.Monitor.Exit(x);
}
The lock keyword marks a statement block as a critical section.
lock(expression) statement_block,
where expression specifies the object that you want to lock on. expression must be a reference type.
Typically, expression is this if you want to protect an instance variable, or typeof(class) if you want to protect a static variable or if the critical section occurs in a static method in the specified class. The statement_block includes the statements of the critical section.

Module 14 (Optional): Threading and Asynchronous Programming |
41 |
|
|
|
|
Demonstration: Using Synchronization Contexts and Synchronized Code Regions to Provide Thread Safety
Topic Objective
To demonstrate the use of synchronization contexts and synchronized code regions to provide thread safety.
Lead-in
In this demonstration, you will see how to achieve thread safety by using synchronization contexts and synchronized code regions.
*****************************ILLEGAL FOR NON-TRAINER USE******************************
This demonstration shows the use of synchronization contexts and synchronized code regions to provide thread safety. The demonstration consists of several test sequences, which are described in this topic.
The code for this demonstration is located in <install folder>\Democode\
Mod14\Demo14.3.
The Unsafe test
The Unsafe test shows the race condition that occurs when multiple threads concurrently execute a method on a class that is not thread-safe.
The CounterUnsafe class has a method that is named Increment, which reads an object’s instance variable that is named count, increments its value, and stores the value back in count. The class and its method do not synchronize access. Therefore, the condition can arise where one thread executing the method has read the value of count but before it can increment it and store it back in count, another concurrently executing thread executing the same method reads the same starting value as the first thread.

42 |
Module 14 (Optional): Threading and Asynchronous Programming |
The Unsafe test should result in output that is similar to the following example. Notice that the Increment method has been called three times but the count has only been incremented by one.
Starting Unsafe Test
Start Resource writing count: 0
Start Resource writing count: 0
Start Resource writing count: 0
Stop Resource writing count: 1
Stop Resource writing count: 1
Stop Resource writing count: 1
All Unsafe threads have completed.
The Static Method Synchronized Context test
The Static Method Synchronized Context test calls the static IncrementStatic method on the CounterSynchronizedContext class, which inherits from
ContextBoundObject and has a [Synchronization()] attribute.
Because synchronization contexts do not synchronize static methods, the following undesirable result occurs:
Starting Static Method Synchronized Context Test
Start Thread:7 Resource writing count, static:0
Start Thread:8 Resource writing count, static:0
Start Thread:9 Resource writing count, static:0
Stop Thread:7 Resource writing count, static:1
Stop Thread:8 Resource writing count, static:1
Stop Thread:9 Resource writing count, static:1
All Static Method Synchronized Context threads have completed.
The Instance Method Synchronized Context test
The Instance Method Synchronized Context test calls the IncrementInstance instance method on the CounterSynchronizedContext class, which inherits from ContextBoundObject and has a [Synchronization()] attribute.
Because synchronization contexts do synchronize instance methods, the following correct result occurs:
Starting Instance Method Synchronized Context Test
Start Object:39 Thread:31 |
Resource writing count, instance:0 |
Stop Object:39 Thread:31 |
Resource writing count, instance:1 |
Start Object:39 Thread:32 |
Resource writing count, instance:1 |
Stop Object:39 Thread:32 |
Resource writing count, instance:2 |
Start Object:39 Thread:33 |
Resource writing count, instance:2 |
Stop Object:39 Thread:33 |
Resource writing count, instance:3 |
All Instance Method Synchronized Context threads have! completed.
Note Unlike a COM STA, a synchronized context is entered by many different threads. Like a COM STA, only one thread at a time can execute the instance methods.