Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

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

.pdf
Скачиваний:
194
Добавлен:
12.02.2016
Размер:
16.87 Mб
Скачать
}}
}}

Module 14 (Optional): Threading and Asynchronous Programming

33

 

 

 

Synchronization Context

Topic Objective

To describe how to synchronize access to instance fields and methods by using synchronization contexts.

Lead-in

Let’s look at using synchronization contexts in more detail.

!SynchronizationAttribute enables simple, automatic synchronization for ContextBoundObject objects

#Only instance fields and methods are synchronized

#Static fields and methods are not protected from concurrent access by multiple threads

[Synchronization()]

[Synchronization()]

class CounterSynchronizedContext : ContextBoundObject class CounterSynchronizedContext : ContextBoundObject

{{

static int sCount = 0; //multiple threads can access static int sCount = 0; //multiple threads can access int iCount = 0; //only one thread can access at a time int iCount = 0; //only one thread can access at a time

public void Increment() { public void Increment() {

//… only one thread can access at a time //… only one thread can access at a time

*****************************ILLEGAL FOR NON-TRAINER USE******************************

A context is an ordered sequence of properties that defines an environment for the objects within it. Contexts are created during the activation process for objects that are configured to require certain automatic services such as synchronization. Multiple objects can reside inside a context.

Using the SynchronizationAttribute attribute

You can use the SynchronizationAttribute attribute to enable simple, automatic synchronization for objects whose class derives from ContextBoundObject. The attribute forces a synchronization domain for the current context and all contexts that share the same instance. When you apply this attribute to an object, only one thread can be executing in all contexts that share an instance of this property. Multiple threads may access the methods and fields, but only a single thread is allowed at any one time. Allowing only a single thread at a time to access instance fields and methods may make these fields and methods thread-safe.

Caution A synchronization context does not protect static fields and methods from concurrent access by multiple threads.

34

Module 14 (Optional): Threading and Asynchronous Programming

For example, consider the CounterSynchronizedContext class. This class’s instanceCount field and Increment method are protected from concurrent access by multiple threads; only one thread at any one time can gain access to these members. However, multiple threads may access its staticCount member, as shown in the following code:

using System;

using System.Threading;

using System.Runtime.Remoting.Contexts;

[Synchronization()]

class CounterSynchronizedContext : ContextBoundObject

{

//Caution! multiple concurrent threads allowed for

//static field – can use manual synchronization to protect static int staticCount = 0;

//only one thread at a time allowed for instance field

int instanceCount = 0;

// only one thread at a time can execute instance method public void Increment()

{

Console.WriteLine(

"Start Object:{0} Thread:{1}!

Resource writing count, static:{2} instance:{3}", this.GetHashCode(), Thread.CurrentThread.GetHashCode(), staticCount, instanceCount);

int tempStaticCount = staticCount;

int tempInstanceCount = instanceCount; Thread.Sleep(50);

tempStaticCount++;

tempInstanceCount++; staticCount = tempStaticCount;

instanceCount = tempInstanceCount; Console.WriteLine(

"Stop Object:{0} Thread:{1}!

Resource writing count, static:{2} instance:{3}", this.GetHashCode(), Thread.CurrentThread.GetHashCode(),

staticCount, instanceCount);

}

}

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.

36

Module 14 (Optional): Threading and Asynchronous Programming

Synchronized Code Regions

Topic Objective

To explain and contrast different options for synchronizing blocks of code.

Lead-in

In addition to synchronizing methods and fields, we also have several options for synchronizing blocks of code:

MethodImplAttribute, the

Monitor class, and the lock keyword.

! MethodImplAttribute with MethodImplOptions.Synchronized

[MethodImplAttribute(MethodImplOptions.Synchronized)]

[MethodImplAttribute(MethodImplOptions.Synchronized)]

#Method executed by only one thread at a time

#Can be used to synchronize instance and static methods

#Similar to using Monitor or Visual C# lock on the entire method

!Monitor - synchronize access to a region of code

#Code region can be a portion of, or the entire method

#Monitor.Enter, Monitor.TryEnter, and Monitor.Exit

#C# lock keyword uses Monitor class methods

lock(this) to protect instance, lock(typeof(class)) to protect static

lock (typeof(Counter)) { lock (typeof(Counter)) {

}}

//block of protected code //block of protected code

*****************************ILLEGAL FOR NON-TRAINER USE******************************

You can decorate a method with a MethodImplAttribute, passing MethodImplOptions.Synchronized, which indicates that only one thread at a time may execute the method. You can use this attribute to synchronize both instance methods and static methods. Using this attribute provides the same result essentially as using Monitor or the Visual C# lock keyword where the synchronized code region encompasses the entire method.

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.

40

Module 14 (Optional): Threading and Asynchronous Programming

For example, to synchronize access in a static method:

using System;

using System.Threading;

class Cache

{

public static void Add(object x)

{

//method code that doesn't require exclusive access lock (typeof(Cache))

{

//code requiring exclusive access to static data

}

//method code that doesn't require exclusive access

}

public static void Remove(object x)

{

//method code that doesn't require exclusive access lock (typeof(Cache))

{

//code requiring exclusive access to static data

}

//method code that doesn't require exclusive access

}

}

For example, to synchronize access in an instance method:

class Counter

{

public override int Read(int threadNum)

{

//...

//method code that doesn't require exclusive access lock(this)

{

//code requiring exclusive access to instance data

}

//method code that doesn't require exclusive access

}

//…

}

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.

Соседние файлы в папке c#