|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
The following article is excerpted from chapter 5 of the book Practical .NET2 and C#2. Contents
Introduction to thread-resource affinityYou can greatly simplify the synchronization of acces to your resources by using the notion of affinity between threads and resources. The idea is to always access a resource using the same thread. Hence, you can remove the need to protect the resource from concurrent access since it is never shared. The .NET framework presents several mechanisms to implement this notion of affinity. The System.ThreadStatic attributeBy default, a static field is shared by all the threads of a process. This behavior forces the developer to synchronize all accesses to such a field. By applying the It is better to avoid directly initializing such a static field during its declaration. In fact, in this case, only the thread which loads the class will complete the initialization of its own version of the field. This behavior is illustrated by the following program: Example1.csusing System.Threading;
class Program {
[System.ThreadStatic]
static string str = "Initial value ";
static void DisplayStr() {
System.Console.WriteLine("Thread#{0} Str={1}",
Thread.CurrentThread.ManagedThreadId , str);
}
static void ThreadProc() {
DisplayStr();
str = "ThreadProc value";
DisplayStr();
}
static void Main() {
DisplayStr();
Thread thread = new Thread( ThreadProc );
thread.Start();
thread.Join();
DisplayStr();
}
}
This program displays the following: Thread#1 Str=Initial value
Thread#2 Str=
Thread#2 Str=ThreadProc value
Thread#1 Str=Initial value
Introduction to Thread Local Storage (TLS)The notion of affinity between threads and resources can also be implemented with the concept of thread local storage (often called TLS). This concept is not new, and exists at the level of Win32. In fact, the .NET framework internally uses this implementation. The concept of TLS uses the notion of a data slot. A data slot is an instance of the static public object GetData( LocalDataStoreSlot slot );
static public void SetData( LocalDataStoreSlot slot, object obj );
TLS named data slotsYou have the possibility of naming a data slot in order to identify it. The static public LocalDataStoreSlot AllocateNamedDataSlot( string slotName );
static public LocalDataStoreSlot GetNamedDataSlot( string slotName );
static public void FreeNamedDataSlot( string slotName );
The garbage collector does not destroy named data slots. This is the responsibility of the developer. The following program uses a named data slot in order to provide a counter to each thread of the process. This counter is incremented for each call to the Example2.csusing System;
using System.Threading;
class Program {
static readonly int NTHREAD = 3; // 3 threads to create.
// 2 calls to fServer() for each thread created.
static readonly int MAXCALL = 2;
static readonly int PERIOD = 1000; // 1 second between calls.
static bool fServer() {
LocalDataStoreSlot dSlot = Thread.GetNamedDataSlot( "Counter" );
int counter = (int) Thread.GetData( dSlot );
counter++;
Thread.SetData( dSlot, counter );
return !( counter == MAXCALL );
}
static void ThreadProc() {
LocalDataStoreSlot dSlot = Thread.GetNamedDataSlot( "Counter" );
Thread.SetData( dSlot, (int) 0 );
do{
Thread.Sleep( PERIOD );
Console.WriteLine(
"Thread#{0} I’ve called fServer(), Counter = {1}",
Thread.CurrentThread.ManagedThreadId ,
(int)Thread.GetData(dSlot));
} while ( fServer() );
Console.WriteLine("Thread#{0} bye",
Thread.CurrentThread.ManagedThreadId );
}
static void Main() {
Console.WriteLine( "Thread#{0} I’m the main thread, hello world",
Thread.CurrentThread.ManagedThreadId );
Thread.AllocateNamedDataSlot( "Counter" );
Thread thread;
for ( int i = 0; i < NTHREAD; i++ ) {
thread = new Thread( ThreadProc );
thread.Start();
}
// We don’t implement a mechanism to wait for child threads
// completion, thus, we are waiting for a while.
Thread.Sleep( PERIOD * (MAXCALL + 1) );
Thread.FreeNamedDataSlot( "Counter" );
Console.WriteLine( "Thread#{0} I’m the main thread, bye.",
Thread.CurrentThread.ManagedThreadId );
}
}
This program displays the following: Thread#1 I’m the main thread, hello world
Thread#3 I’ve called fServer(), Counter = 0
Thread#4 I’ve called fServer(), Counter = 0
Thread#5 I’ve called fServer(), Counter = 0
Thread#3 I’ve called fServer(), Counter = 1
Thread#3 bye
Thread#4 I’ve called fServer(), Counter = 1
Thread#4 bye
Thread#5 I’ve called fServer(), Counter = 1
Thread#5 bye
Thread#1 I’m the main thread, bye.
TLS anonymous data slotsYou can call the Example3.csusing System;
using System.Threading;
class Program {
static readonly int NTHREAD = 3; // 3 threads to create.
// 2 calls to fServer() for each thread created.
static readonly int MAXCALL = 2;
static readonly int PERIOD = 1000; // 1 second between calls.
static LocalDataStoreSlot dSlot;
static bool fServer() {
int counter = (int) Thread.GetData( dSlot );
counter++;
Thread.SetData( dSlot, counter );
return !( counter == MAXCALL );
}
static void ThreadProc() {
Thread.SetData( dSlot, (int) 0 );
do{
Thread.Sleep(PERIOD);
Console.WriteLine(
"Thread#{0} I’ve called fServer(), Counter = {1}",
Thread.CurrentThread.ManagedThreadId ,
(int)Thread.GetData(dSlot));
} while ( fServer() );
Console.WriteLine( "Thread#{0} bye",
Thread.CurrentThread.ManagedThreadId );
}
static void Main() {
Console.WriteLine( "Thread#{0} I’m the main thread, hello world",
Thread.CurrentThread.ManagedThreadId );
dSlot = Thread.AllocateDataSlot();
for ( int i = 0; i < NTHREAD; i++ ) {
Thread thread = new Thread( ThreadProc );
thread.Start();
}
Thread.Sleep( PERIOD * (MAXCALL + 1) );
Console.WriteLine( "Thread#{0} I’m the main thread, bye.",
Thread.CurrentThread.ManagedThreadId );
}
}
The System.ComponentModel.ISynchronizeInvoke interfaceThe public object System.ComponentModel.ISynchronizeInvoke {
public object Invoke( Delegate method, object[] args );
public IAsyncResult BeginInvoke( Delegate method, object[] args );
public object EndInvoke( IAsyncResult result );
public bool InvokeRequired{ get; }
}
An implementation of this interface can make sure that certain methods are always executed by the same thread, in a synchronous or asynchronous way:
The You can develop your own implementations of the
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||