This recipe will show you how to perform basic atomic operations on an object to prevent the race condition without blocking threads.
using System;
using System.Threading;
//
// Performing basic atomic operations
//
// This recipe will show you how to perform basic atomic operations on an object
// to prevent the race condition without blocking threads.
//
// How it works...
//
// When the program runs, it creates three threads that will execute a code in
// the TestCounter method. This method runs a sequence of increment/decrement
// operations on an object. Initially, the Counter object is not thread-safe and
// we get a race condition here. So in the first case, a counter value is not
// deterministic. We could get a zero value; however, if you run the program
// several times, you will eventually get some incorrect nonzero result.
//
// In Chapter 1, Threading Basics, we resolved this problem by locking our
// object, causing other threads to block while one thread gets the old counter
// value, then computes and assigns a new value to the counter. However, if we
// execute this operation in such a way, it cannot be stopped midway; we would
// achieve the proper result without any locking with the help of the
// Interlocked construct. It provides the atomic methods Increment, Decrement,
// and Add for basic math, and it helps us to write the Counter class without
// the use of locking.
//
namespace Chapter2.Recipe1
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("Incorrect counter");
var c = new Counter();
var t1 = new Thread(() => TestCounter(c));
var t2 = new Thread(() => TestCounter(c));
var t3 = new Thread(() => TestCounter(c));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("Total count: {0}", c.Count);
Console.WriteLine("--------------------------");
Console.WriteLine("Correct counter");
var c1 = new CounterNoLock();
t1 = new Thread(() => TestCounter(c1));
t2 = new Thread(() => TestCounter(c1));
t3 = new Thread(() => TestCounter(c1));
t1.Start();
t2.Start();
t3.Start();
t1.Join();
t2.Join();
t3.Join();
Console.WriteLine("Total count: {0}", c1.Count);
}
static void TestCounter(CounterBase c)
{
for (int i = 0; i < 100000; i++)
{
c.Increment();
c.Decrement();
}
}
class Counter : CounterBase
{
private int _count;
public int Count
{
get
{
return _count;
}
}
public override void Increment()
{
_count++;
}
public override void Decrement()
{
_count--;
}
}
class CounterNoLock : CounterBase
{
private int _count;
public int Count
{
get
{
return _count;
}
}
public override void Increment()
{
Interlocked.Increment(ref _count);
}
public override void Decrement()
{
Interlocked.Decrement(ref _count);
}
}
abstract class CounterBase
{
public abstract void Increment();
public abstract void Decrement();
}
}
}