ThreadState

Hello, as promised this is a continuation of Part II of the C# threading series. I hope you your time is well spent here.

Locking
Exclusive locking ensures that only one thread can access a particular section of the code at any given time. The main mechanisms used for exclusive locking are lock and mutex.
For non-exclusive locking semaphores are used. The difference between the two is that while locking is easier and faster, mutex allows to span locking across applications and different processes, for instance a common resource like a file.

[code lang=”csharp”]
class Test{
int x=5;

void Calc(){
x+=5;
Console.WriteLine(x);
}
}
[/code]

This class is not thread-safe and the output is unpredictable. The thread safe code is to be found below and makes use of locks.

[code lang=”csharp”]
class Test{
int x=5;
object _lock = new object();

void Calc(){
lock(_locker){
x+=5;
Console.WriteLine(x);
}
}
}
[/code]

Only one thread can lock the synchronizing object at a time, while any other threads are blocked until the lock is released.
If other threads are trying to lock the object they’re going to be served in a queue but with no guaranteed order. Threads that are waiting for accessing a locked area are in a WaitSleepJoin state.
There is a trick here: if an exception happens in the lock section then that part of the code won’t be released and other threads will be waiting for ever. Aside from that there are more subtle details here on Monitors but won’t get into that now.

Deadlocking is one of the hardest problems in multi-threading, especially when there are many objects that interact with each other. The main problem is that you can’t be sure what locks your caller has taken out.
So, you might lock a private field a within your class X, unaware that your caller (or caller’s caller) has already locked the field b within class Y. Meanwhile, another thread is doing exactly the reverse, creating a deadlock.
Another example of deadlocking arises when calling Dispatcher.Invoke (in WPF) or Control.Invoke (in Windows Forms) while in possession of a lock. If the UI happens to be running another method that’s waiting on the same lock, a deadlock will occur. This can often be fixed simply by calling BeginInvoke instead of Invoke. Alternatively, you can release your lock before calling Invoke, although this won’t work if your caller took out the lock.
So be careful, locking is great but dangerous and as far as performance goes, locking is fast, nanoseconds fast.

Mutex, yey!
They basically do the same thing as locks but across processes. One common use case is to ensure that an application runs on a machine as a single instance.
Performance-wise mutex is slower than lock and it’s done in order of microseconds.

[code lang=”csharp”]
class Test
{
void Main()
{
// Naming a mutex makes it available computer-wide.
// Ensure you have a uniqure name for it.
using (var mutex = new Mutex(false, "nameOfTheMutex"))
{
// Wait a few seconds in case there is another
// application in the process of shutting down.
if (!mutex.WaitOne(TimeSpan.FromSeconds(3), false))
{
Console.WriteLine("Another instance is running.");
return;
}
RunMyApp();
}
}

void RunMyApp()
{
Console.WriteLine("Running. Press Enter to exit");
Console.ReadLine();
}
}
[/code]

Semaphores or in other words traffic lights.
They have a limited capacity defined by user, once full everyone has to wait outside until someone leaves.
They’re particularly useful when one wants to limit the maximum number of threads that can execute a piece of code at the same time.
Like mutex, a semaphore can span across multiple processes by naming it and thus limiting the number of applications that can run in parallel.
Below you can find an example for semaphore usage:

[code lang=”csharp”]
class Test
{
// define a capacity of three
private static Semaphore _semaphore = new Semaphore(0, 3);

void Main()
{
for (int i = 1; i <= 5; i++) new Thread(TestThread).Start(i);
}

void TestThread(object id)
{
Console.WriteLine(id + " tries to get in");
_semaphore.WaitOne();
Console.WriteLine(id + " is in!");
Thread.Sleep(1000 * (int)id);
Console.WriteLine(id + " leaves");
_semaphore.Release();
}
}
[/code]

That’s all, see you on the next posts on Thread Safety and Concurrent Collections!

Simple Share Buttons