# Luna Tech

Tutorials For Dummies.

# Race Condition

Be careful when sharing variables and resouces in a parallel process as you will run into race conditions.

• we need to make sure only one thread at a time is changing the shared variable
• we need to use something that allows us to change the value in a thread-safe manner.

## Solution

1. atomic operations
2. lock

Note: Always perfer atomic operations over lock when possible as its less overhead and performs faster. If not possible, we need to introduce a locking mechanism.

# How to use atomic operations with the interlock class?

Suitable for: operations on an integer to increment, decrement or add.

Tool: System.Threading has a class called interlocked, we can use it to perform atomic operations on 32-bit and 64-bit integers.

Note: Interlocked uses less instructions and is faster than a lock.

void Main()
{
var stopwatch = new Stopwatch();
stopwatch.Start();

int total = 0;

Parallel.For(0, 100, (i) =>
{
var result = Compute(i);
});

Console.WriteLine(total);
Console.WriteLine($"It took: {stopwatch.ElapsedMilliseconds}ms to run"); } static Random random = new Random(); static int Compute(int value) { var randomMilliseconds = random.Next(10, 50); var end = DateTime.Now + TimeSpan.FromMilliseconds(randomMilliseconds); while (DateTime.Now < end) { } return value + 100; }  # How to introduce a lock? What is a deadlock and how to avoid it from happening. We can introduce a lock object, only one thread at a time can run the code inside the lock statement. If we cannot use interlocked because of data type, then we have to use lock. Considerations: 1. we need to be careful when adding a lock, as it may lead to deadlocks, especially when you’re using nested locks 2. only lock for as short of a time as possible, do as little work as possible in the lock statement • calling an expensive operation inside a lock is not recommended as it forces other threads to wait void Main() { var stopwatch = new Stopwatch(); stopwatch.Start(); decimal total = 0; // non-parallel version // for(int i = 0; i < 100; i++){ // total += Compute(i); // } // This is slow as the computation happens inside the lock // Parallel.For(0, 100, (i) => { // lock (syncRoot){ // total += Compute(i); // } // }); // This is faster as the computation happens outside the lock Parallel.For(0, 100, (i) => { var result = Compute(i); lock (syncRoot){ // the lock time should be short! total += result; } }); Console.WriteLine(total); Console.WriteLine($"It took: {stopwatch.ElapsedMilliseconds}ms to run");
}

static object syncRoot = new object();
static Random random = new Random();
static decimal Compute (int value) {
var randomMilliseconds = random.Next(10, 50);
var end = DateTime.Now + TimeSpan.FromMilliseconds(randomMilliseconds);

while (DateTime.Now < end) {}

return value + 0.5m;
}