Thread Synchronization

Source Code

All source code can be found on GitHub here.

This is part of my HowTo in .NET seriies. An overview can be seen here,


Concurrency

Concurrency exists due to multiple threads touching the same resource at the same time.

A dirty read occurs when the state of one entity is read by one thread while another thread has started but not finished changing it.

Dirty reads can be prevented by locking regions of code to be accessed by only one thread at a time. This process is called thread synchronisation and unfortunately can lead to deadlock and resource starvation.

Deadlock occurs when two threads have exclusive access to a synchronised resource that is required by the other; it is a stalemate situation.

Resource starvation occurs when one thread is continuously denied access to a resource it is trying to access.

Thread synchronization prevents dirty reads. There are many ways to achieve this in .NET.

Lock Regions

The lock region defines a region of code where only one thread can access at a time.

public class LockRegion
{
    private System.Object lockControl = new System.Object ();

    public void AMethod ()
    {
        lock (lockControl) {
            // Access to this section permitted to only one thread at a time.
            // read or set shared state here etc.
        }
    }
}

The parameter passed into the lock method defines the scope of thread synchronisation. It can be any reference type. To avoid deadlock it is advised to avoid:

* Using the same lock objects between threads
* Public members and any entities which will not be defined as unique
* Do not lock on ‘this’; it could be used by code external to your class.
* Strings should not be used as lock objects due to string-interning. This is where one string is only actually generated internally for multiple string instances with the same value.

A thread is paused if it cannot get unique access to a region of code; i.e If another thread is running in the region. It will be resumed when the region is free where it will try again to get unique access.

ThreadInterruptedException is thrown if a thread is interrupted while waiting to enter a lockregion.

Monitor Class

The Lock keyword is a shortcut to System.Thread.Monitor class. Upon entering a lock region Monitor.Enter() is called while Monitor.Exit() is called upon leaving.

The lock example previously could be rewritten with the monitor class:

      
private System.Object lockControl = new System.Object ();

public void LockRegion ()
{
    try {

        Monitor.Enter (lockControl);

        try {

            // Access to this section permitted to only one thread at a time.
            // Read or set shared state here etc. 
        } finally {
            Monitor.Exit (lockControl);
        }
    } catch (SynchronizationLockException ex) {
    }
}

Accessing Monitor directly allows greater control over what happens to a thread when requesting or being denied unique access to a lock region.

TryEnter

Monitor.TryEnter() returns a boolean indicating if the request for lock was successfully acquired. This can be useful to prevent the thread being paused when access is denied, such as is the case with lock and Monitor.Enter. It has an override for the maximum wait time in milliseconds before it returns false:

public void TryEnterExampple ()
{
    if (Monitor.TryEnter (lockControl, 100)) {
        try {
        } finally {
            Monitor.Exit (lockControl);
        }     
    } else {
        // Required logic for failure to get unique access.    
    }
}

Pulse & Wait

The Monitor.Wait() method allows a thread to release unique access to a locked region of code while it is still inside. The thread will be paused and another thread will gain unique access. Once the other thread has released the lock the paused thread is free to try and gain a unique lock again.

Wait provides an override which can define the minimum amount of time in milliseconds it will wait before trying to get unique access once again; i.e the minimum time it will vacate the region.

Pulse and PulseAll can be called by a thread which has unique access to a locked region. It does not pause the thread but rather signals that it is about to leave the locked region.

Interlocked Class

System.Threading.Interlocked allows synchronization of threads by exposing a series of atomic operations which operate on numeric member fields.

Increment, decrement and addition operations:

Interlocked.Increment (ref aNumber);

Interlocked.Decrement (ref aNumber);

Interlocked.Add (ref aNumber, 10);

The exchange function can be used to swap two entities:

Interlocked.Exchange (ref usingResource, 1);

The exchange function can be used to provide a manual lock mechanism:

private int usingResource = 0;

if (Interlocked.Exchange (ref usingResource, 1) == 0) {     
    // unlock by setting back to 0
    Interlocked.Exchange (ref usingResource, 0); 
}    

ThreadStatic

The ThreadStatic attribute can be used to mark member fields as unique to a thread:

[ThreadStatic] static double previous = 0.0;

ThreadLocal

.NET Framework 4 offers a ThreadLocal.

Local variables defined with ThreadLocal will be not only be unique and private to each thread, they are also initialized lazily upon first consumption within each thread.

var threadLocal = new ThreadLocal<int> (() => {
    return 1;
});

var isIsValueCreated = threadLocal.IsValueCreated;
var value = threadLocal.Value;

Synchronization Attribute

The Synchronization Attribute allow automatic thread synchronization at class or method level.

The Synchronization Attribute can be found within the namespace System.Runtime.Remoting.Contexts.

The CLR places the class into its own AppDomain Context, though you need to inherit from ContextBoundObject:

[Synchronization] 
public class SynchronizationAttributeExample : ContextBoundObject
{
}

Thread synchronization is handled by the CLR however there is not much control as all the code within the scope of the attribute is synchronized which is not good for performance.

Thread.Join

Thread.Join can be used to pause a thread until a spawned thread has finished executing. The calling thread will be blocked until a spawned thread terminates, if it has already terminated it will return immediately.

Thread t1 = new Thread (() => { 
    Thread.Sleep (1000);
});

Thread t2 = new Thread (() => { 
    Thread.Sleep (2000);
});

t1.Start ();
t2.Start ();

t1.Join ();
t2.Join ();

AutoResetEvent

AutoResetEvent allows coordinating multiple threads by pausing and resuming them. This can allow them to synchronise their work.

The AutoResetEvent class can coordinate a main thread and a new thread by passing signals between the two threads.

Calling WaitOne on the secondary thread will pause the thread until the main thread has signalled with the Set method.

Set does not cause the main thread to pause but rather allows the secondary thread to resume.

private AutoResetEvent autoResetEvent = new AutoResetEvent (false);

void Main ()
{
    var aThread = new Thread (
                      new ThreadStart (OtherThreadMethod));

    aThread.Start ();
    autoResetEvent.Set ();        

    // some waiting logic would be in here
    Thread.Sleep (100);

    autoResetEvent.Set ();
}

void OtherThreadMethod ()
{
    autoResetEvent.WaitOne (); // pauses until set is called by main thread
    autoResetEvent.WaitOne ();
}

AutoResetEvent takes a boolean which determines if the event starts of as signaled (having Set called). If true the secondary thread will execute immediately, if false it will not start executing until Set is called.

Processes, Threads, AppDomains And Object Contexts

Source Code

All source code can be found on GitHub here.

This is part of my HowTo in .NET seriies. An overview can be seen here,

Processes

Any code executing on a windows platform is wrapped up in a process.

Each process is given a unique PID which can be seen within the Task Manager.

If the code entity being run is determined to be a .NET assembly, an instance of the CLR is created to run the assembly.

Each instance of the CLR receives its own managed heap.

Prior to .NET V4 a process could only host one instance of the CLR. Though this has now been changed, each instance of the CLR still receives its own instance of a managed heap memory allocation.

Each .NET process is run in isolation from other processes; they cannot access each other’s resources such as memory. The separation means that if one process errors it does not bring down other running processes.

Querying Running Processes

The Process class provides a number of static methods for retrieving running processes on either a local or remote computer.

Get all locally running processes on a local machine:

Process[] pids = Process.GetProcesses();

Get all instances of a running application by its name:

Process [] pids = Process.GetProcessesByName("MyApp");

Get all instances of a running application on a remote machine either by the machine name or by IP address:

Process [] pids = Process.GetProcessesByName("MyApp", "Server");


Process [] pids = Process.GetProcessesByName("MyApp", "1.2.3.4");

Get a process by its process id:

Process aProcess = Process.GetProcessById(123);

Retrieve the process name of a retrieved Process instance:

var aName = aProcess.ProcessName;

Starting & Killing Processes

A process can be killed by simply calling Kill on the process instance:

aProcess.Kill ();

The Process class offers the static Start method to start a process by an executable name.

Process.Start("AnApp.exe");

Command line parameters can be passed to the application:

Process.Start("AnApp.exe", "parameter");

Process can take a number of configurations:

Process.Start (new ProcessStartInfo ("AnApp.exe", "AParameter") { 
    CreateNoWindow = true, 
    WorkingDirectory = @"c:\" 
});

Threads

A thread is the smallest sequence of instructions that can be independently managed by an operating system and therefore processed by a processor.

Before the luxury of multi core systems only one thread could be executed at a time. Multiple threads would have to share processor time. Concurrent thread execution would seemingly be achieved by continuously pausing and resuming each running thread on the system.

In todays world of multi core systems multiple threads can genuinely run simultaneously, however seeing as there can be more threads than processors, the same principle holds true.

With multiple threads running on a computer, the processor will give more processor time to threads marked as having a higher priority.

When a Process is started, a default thread is created or spawned which will execute the code associated with the assembly.

Each thread receives it’s call stack; all local variables are placed on the stack and as such are private to the thread.

Thread Members

Code can get access to the thread that is running it with the CurrentThread property:

var aThread = Thread.CurrentThread;

Access to all threads running within a process via the Threads property of a Process instance:

var threads = Process.GetProcesses () [0].Threads;

Determine if the thread is still alive; that it has been started and has not terminated normally or aborted:

var isAlive = aThread.IsAlive;

The name of the thread can be set and retrieved with the name property. This can be helpful to identify which thread is which; the name is shown within the Threads debug window:

aThread.Name = "Thready McFreddy";
var aName = aThread.Name;

Foreground & Background Threads

The CLR has the concept of foreground and background threads.

Foreground Threads can prevent the CLR terminating or unloading an AppDomain.

Background threads can not prevent the CLR from terminating.

A thread can be switched between foreground and background with the IsBackground property:

aThread.IsBackground = true;

Thread Priority

A thread’s priority can be changed to request more or less processing time from the CLR. The higher the priority the more time a thread will be given from a processor. The default priority is normal:

* Lowest
* BelowNormal
* Normal
* AboveNormal
* Highest

To get a threads priority:

var priority = aThread.Priority;

The thread priority can be changed at any point in the threads existence.

aThread.Priority = ThreadPriority.Highest;

Thread State

The state of a thread can be determined via the ThreadState property which returns a member of the ThreadState enum:

var threadState = aThread.ThreadState;

Name
Description
Aborted
The thread has been aborted or requested to be aborted . The thread is dead however its status has not changed to stopped.
AbortRequested
The thread has been requested to abort by Thread.Abort method but it has not received the System.Threading.ThreadAbortException which will terminate it.
Background
The thread is a background thread.
Running
The thread is running, is not blocked and no attempt to terminate has been made.
Stopped
The thread has been completely terminated or stopped.
StopRequested
A request to stop the thread has been made.
Suspended
The thread is suspended.
SuspendRequested
A request to suspend the thread has been made.
Unstarted
The thread has not been started via the Thread.Start method.
WaitSleepJoin
The thread is paused due to either a Thread.Sleep or because a request to get exclusive access to a locked region has been made.

Thread Abort

Thread.Abort can be used to stop an executing thread. It will cause a ThreadAbort-Exception to be thrown. Caution should be used when calling this method as it can leave a thread and it’s data in a corrupt state.

Application Domains

An application domain is a segmentation within a process; it provides a level of isolation and security within each process.

AppDomains abstract the OS from the executing code.

Threads are actually associated with a single AppDomain and not a Process.

The CLR creates a default AppDomain and its default Thread as part of executing a .NET assembly.

A Process can contain multiple AppDomains and AppDomains can contain multiple Threads; however these will only exist if if they are created manually by executing code within the Process.

AppDomains are less expensive than Processes; the CLR can load or unload AppDomains quicker than a processes can be created or destroyed.

When an Assembly is loaded it is loaded into an AppDomain. Code within an assembly can only be executed within the AppDomain it was loaded into.

A thread can only run inside one AppDomain at a time though it can be moved between AppDomains when it is seen fit by the CLR, this is to allow thread reuse. A thread can only execute code associated to the AppDomain it is currently running in.

AppDomain Members

Executing code can get access to the domain it is running in via the CurrentDomain property:

AppDomain aAppDomain = AppDomain.CurrentDomain;

An AppDomain instance exposes properties describing itself:

var id = aAppDomain.Id;

var name = aAppDomain.FriendlyName;

var isDefault = aAppDomain.IsDefaultAppDomain ();

var dir = aAppDomain.BaseDirectory;

GetAssemblies lists all .NET assemblies which an AppDomain has loaded into it:

Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies (););

Creating & Loading AppDomains

AppDomains can be created:

var aDomain = AppDomain.CreateDomain("MyDomain"); 

Loading and executing assemblies into AppDomains:

AppDomain.Load ("Foo");

AppDomain.ExecuteAssembly ("Foo.exe");

An AppDomain allows subscribing to the AssemblyLoad event which is triggered when an assembly is loaded into it:

aaAppDomain.AssemblyLoad += (o, s) => {
            };   

Unloading AppDomains

Individual assemblies can not be unloaded though AppDomains can:

AppDomain.Unload(aAppDomain);

An AppDomain allows subscribing to the DomainUnload event which is triggered when it is being unloaded:

aAppDomain.DomainUnload += (o, s) => { };    

Process.ProcessExit event is triggered when the Process is exited:

aAppDomain.ProcessExit += (o, s) =>   { };

Object Context Boundaries

An AppDomain splits itself into contexts. The CLR groups types into groups depending upon their context needs.

Most types are placed into the default context; this is referred to as context 0 and the objects contained within as context-agile objects.

CLR creates new contexts when a loaded type requires a new context boundary. Objects requiring thread synchronization typically require this process. This is covered in the the multithreading section.

Creating a Context Bound Object

Objects requiring special context needs must derive from System.ContextBoundObject base.

public class ContextBoundObjectExample : ContextBoundObject
{
}

Inspecting An Object’s Context

var context = Thread.CurrentContext;

var contextId = context.ContextID;

var contextProperties = context .ContextProperties;

var propname = contextProperties[0].Name;
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s