using System;
using System.Threading;
abstract class Counter
{
protected int count = 0;
public abstract int Read(int threadNum);
public abstract void Increment(int threadNum);
}
// Note that the instance variable count is shared between the two methods Read
// and Increment. Threads concurrently executing one or both of these methods can
// interfere with each other unless action is taken to synchronize access
class CounterUnsafe : Counter
{
public override int Read(int threadNum)
{
// following code is not thread-safe and has a race condition
try
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
}
}
public override void Increment(int threadNum)
{
// following code is not thread-safe and has a race condition
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
// the following four lines simulate count++ with a very large
// window of time between count being read and being incremented.
// This large window ensures that the race condition will create
// errors often when the code is accessed concurrently by multiple threads.
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
finally
{
}
}
}
class CounterUsingInterlocked : Counter
{
public override int Read(int threadNum)
{
try
{
// following is thread safe but since Increments can occur during
// this method, the value of count can change between the start
// of the method and its return/end, if this is a problem
// you need to use manual locks or a synchronized context.
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
// Note that count++ is not an atomic operation and therefore not thread-safe.
// With count++ there would be a window of time between reading the value count and
// incrementing the value of count during which time another thread could execute and
// also read and/or change the value of count. Even thought this window
// is much smaller than with the CounterUnsafe methods this non atomic operation
// still results in a race condition where an increment can be lost.
// To make the increment of count thread-safe we use an atomic Interlocked.Increment call.
int tempCount = Interlocked.Increment(ref count);
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, tempCount);
}
finally
{
}
}
}
class CounterUsingLock : Counter
{
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
lock(this)
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
lock(this)
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
// rest of method code that doesn't require exclusive access
}
}
class CounterUsingMutex : Counter
{
Mutex m = new Mutex();
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
m.WaitOne();
try
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
m.ReleaseMutex();
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
m.WaitOne();
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
finally
{
m.ReleaseMutex();
}
// rest of method code that doesn't require exclusive access
}
}
class CounterUsingReaderWriterLock : Counter
{
ReaderWriterLock rwl = new ReaderWriterLock();
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
rwl.AcquireReaderLock(Timeout.Infinite);
try
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
rwl.ReleaseReaderLock();
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
rwl.AcquireWriterLock(Timeout.Infinite);
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
finally
{
rwl.ReleaseWriterLock();
}
// rest of method code that doesn't require exclusive access
}
}
class App
{
static Counter counter = null;
static int totalNumberOfAsyncOps = 10;
static int numAsyncOps = totalNumberOfAsyncOps;
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
public static void Main()
{
Console.WriteLine("\n\nUnsafe test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUnsafe();
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Unsafe threads have completed.");
Console.WriteLine("\n\nInterlocked test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingInterlocked();
for (int threadNum = 0; threadNum < totalNumberOfAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Interlocked threads have completed.");
Console.WriteLine("\n\nLock test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingLock();
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Lock threads have completed.");
Console.WriteLine("\n\nMutex test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingMutex();
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Mutex threads have completed.");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingReaderWriterLock();
Console.WriteLine("\n\nReadWriteLock test:");
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All ReadWriteLock threads have completed.");
}
// The callback method's signature MUST match that of a System.Threading.TimerCallback
// delegate (it takes an Object parameter and returns void)
static void UpdateResource(Object state)
{
int threadNum = (int) state;
if ((threadNum % 2) != 0) counter.Read(threadNum);
else counter.Increment(threadNum);
if (( Interlocked.Decrement(ref numAsyncOps)) == 0)
asyncOpsAreDone.Set();
}
}
using System.Threading;
abstract class Counter
{
protected int count = 0;
public abstract int Read(int threadNum);
public abstract void Increment(int threadNum);
}
// Note that the instance variable count is shared between the two methods Read
// and Increment. Threads concurrently executing one or both of these methods can
// interfere with each other unless action is taken to synchronize access
class CounterUnsafe : Counter
{
public override int Read(int threadNum)
{
// following code is not thread-safe and has a race condition
try
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
}
}
public override void Increment(int threadNum)
{
// following code is not thread-safe and has a race condition
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
// the following four lines simulate count++ with a very large
// window of time between count being read and being incremented.
// This large window ensures that the race condition will create
// errors often when the code is accessed concurrently by multiple threads.
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
finally
{
}
}
}
class CounterUsingInterlocked : Counter
{
public override int Read(int threadNum)
{
try
{
// following is thread safe but since Increments can occur during
// this method, the value of count can change between the start
// of the method and its return/end, if this is a problem
// you need to use manual locks or a synchronized context.
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
// Note that count++ is not an atomic operation and therefore not thread-safe.
// With count++ there would be a window of time between reading the value count and
// incrementing the value of count during which time another thread could execute and
// also read and/or change the value of count. Even thought this window
// is much smaller than with the CounterUnsafe methods this non atomic operation
// still results in a race condition where an increment can be lost.
// To make the increment of count thread-safe we use an atomic Interlocked.Increment call.
int tempCount = Interlocked.Increment(ref count);
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, tempCount);
}
finally
{
}
}
}
class CounterUsingLock : Counter
{
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
lock(this)
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
lock(this)
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
// rest of method code that doesn't require exclusive access
}
}
class CounterUsingMutex : Counter
{
Mutex m = new Mutex();
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
m.WaitOne();
try
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
m.ReleaseMutex();
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
m.WaitOne();
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
finally
{
m.ReleaseMutex();
}
// rest of method code that doesn't require exclusive access
}
}
class CounterUsingReaderWriterLock : Counter
{
ReaderWriterLock rwl = new ReaderWriterLock();
public override int Read(int threadNum)
{
// ...
// method code that doesn't require exclusive access
rwl.AcquireReaderLock(Timeout.Infinite);
try
{
Console.WriteLine(
"Start Resource reading (Thread={0})count: {1}", threadNum, count);
Thread.Sleep(250);
Console.WriteLine(
"Stop Resource reading (Thread={0}) count: {1}", threadNum, count);
return count;
}
finally
{
rwl.ReleaseReaderLock();
}
}
public override void Increment(int threadNum)
{
// ...
// method code that doesn't require exclusive access
rwl.AcquireWriterLock(Timeout.Infinite);
try
{
Console.WriteLine(
"Start Resource writing (Thread={0}) count: {1}", threadNum, count);
int tempCount = count;
Thread.Sleep(1000);
tempCount++;
count = tempCount;
Console.WriteLine(
"Stop Resource writing (Thread={0}) count: {1}", threadNum, count);
}
finally
{
rwl.ReleaseWriterLock();
}
// rest of method code that doesn't require exclusive access
}
}
class App
{
static Counter counter = null;
static int totalNumberOfAsyncOps = 10;
static int numAsyncOps = totalNumberOfAsyncOps;
static AutoResetEvent asyncOpsAreDone = new AutoResetEvent(false);
public static void Main()
{
Console.WriteLine("\n\nUnsafe test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUnsafe();
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Unsafe threads have completed.");
Console.WriteLine("\n\nInterlocked test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingInterlocked();
for (int threadNum = 0; threadNum < totalNumberOfAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Interlocked threads have completed.");
Console.WriteLine("\n\nLock test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingLock();
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Lock threads have completed.");
Console.WriteLine("\n\nMutex test:");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingMutex();
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All Mutex threads have completed.");
asyncOpsAreDone.Reset();
numAsyncOps = totalNumberOfAsyncOps;
counter = new CounterUsingReaderWriterLock();
Console.WriteLine("\n\nReadWriteLock test:");
for (int threadNum = 0; threadNum < numAsyncOps; threadNum++)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(UpdateResource), threadNum);
}
asyncOpsAreDone.WaitOne();
Console.WriteLine("All ReadWriteLock threads have completed.");
}
// The callback method's signature MUST match that of a System.Threading.TimerCallback
// delegate (it takes an Object parameter and returns void)
static void UpdateResource(Object state)
{
int threadNum = (int) state;
if ((threadNum % 2) != 0) counter.Read(threadNum);
else counter.Increment(threadNum);
if (( Interlocked.Decrement(ref numAsyncOps)) == 0)
asyncOpsAreDone.Set();
}
}