4. Synchronization

Synchronization in Multithreading

Multithreading allows multiple threads to execute simultaneously. However, it introduces the issue of multiple threads accessing and modifying shared data at the same time, which leads to data inconsistency. This issue is known as the synchronization problem.

Synchronization Problem

In a multithreaded environment, the synchronization problem occurs when two threads attempt to modify shared data concurrently. This can lead to unexpected and inconsistent results.

Example:

public class Counter {
    public int Count = 0;
}

public class ThreadSample {
    Counter _counter;

    public ThreadSample(Counter counter) {
        _counter = counter;
    }

    public void Count() {
        for (int i = 0; i < 10000; i++) {
            _counter.Count++;
        }
    }
}

Without synchronization, if two threads run the Count method on the same Counter object, the final Count value may not be 20000 due to the synchronization problem.

Synchronization Techniques

The solution to the synchronization problem is to isolate the shared data when it is being modified. This can be achieved using various synchronization techniques:

Interlocked Class

The Interlocked class provides methods for atomic operations, which are performed in a single, unbroken step that cannot be interrupted by other threads.

Example:

public void Count() {
    for (int i = 0; i < 10000; i++) {
        Interlocked.Increment(ref _counter.Count);
    }
}

Monitor Class

The Monitor class provides a mechanism that synchronizes access to blocks of code. A thread enters the monitor by calling the Monitor.Enter method and exits it by calling the Monitor.Exit method. Only one thread can execute inside the monitor at a time.

Example:

public void Count() {
    Monitor.Enter(_counter);
    try {
        for (int i = 0; i < 10000; i++) {
            _counter.Count++;
        }
    } finally {
        Monitor.Exit(_counter);
    }
}

Lock Keyword

The lock keyword in C# is a syntactic shortcut for a Monitor.Enter / Monitor.Exit block. It ensures that one thread does not enter a critical section of code while another thread is in the middle of the same code block.

Example:

public void Count() {
    lock (_counter) {
        for (int i = 0; i < 10000; i++) {
            _counter.Count++;
        }
    }
}

Mutex Class

The Mutex class is similar to a Monitor, but it works across multiple processes, meaning it can be used to synchronize threads in different applications. It can be named or unnamed. An unnamed Mutex is local to the application, while a named Mutex can be used across the system.

Example:

public class MutexSample {
    static Mutex _mutex = new Mutex();

    public void Run() {
        _mutex.WaitOne();
        try {
            // Critical section
            for (int i = 0; i < 10000; i++) {
                _counter.Count++;
            }
        } finally {
            _mutex.ReleaseMutex();
        }
    }
}

In this example, the Mutex ensures that only one thread can enter the critical section at a time, even if the threads are from different processes.

Reference

The content in this document is based on the original notes provided in Azerbaijani. For further details, you can refer to the original document using the following link:

Original Note - Azerbaijani Version