Intra-process vs Inter-process Synchronization in C#

Intra-process synchronization and inter-process synchronization are distinct methods for controlling access to shared resources, each functioning at varying levels.

Intra-process Synchronization

Intra-process synchronization involves the coordination of shared resources within a single process. It requires multiple threads within the same process to manage access to shared data or resources to avoid race conditions and maintain data integrity. Various methods for intra-process synchronization in C# include lock (Monitor), Mutex, Semaphore, SemaphoreSlim, and ReaderWriterLockSlim.

Example Scenario 1

Consider a web application where multiple threads handle incoming HTTP requests and need to read from or write to a shared in-memory cache.

internal class SharedInMemoryCache
{
    private readonly object lockObject = new object();
    private Dictionary<int, string> cache = new Dictionary<int, string>();

    public string GetItem(int key)
    {
        lock (lockObject)
        {
            if (cache.TryGetValue(key, out string value))
            {
                return value;
            }
            return null;
        }
    }

    public void AddItem(int key, string value)
    {
        lock (lockObject)
        {
            cache[key] = value;
        }
    }
}

Example Scenario 2

Within this situation, there exists a customer information database in which various threads (representing distinct users) execute read and write tasks on the customer data. It is imperative to synchronize these tasks to prevent race conditions and uphold data integrity.

using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SharedResourceMechanism
{
    internal class CustomerInformation
    {
        private readonly object lockObject = new object();

        public void ReadCustomerDetails(int customerId)
        {
            lock (lockObject)
            {
                using (SqlConnection connection = new SqlConnection("your_connection_string")) // Write your connection string here
                {
                    connection.Open();
                    SqlCommand command = new SqlCommand("SELECT * FROM Customers WHERE CustomerID = @CustomerID", connection);
                    command.Parameters.AddWithValue("@CustomerID", customerId);
                    SqlDataReader reader = command.ExecuteReader();
                    while (reader.Read())
                    {
                        // Proceed with writing the code to process the data.
                    }
                }
                // The lock will be released in this area.
            }
        }

        public void WriteCustomerDetails(int customerId, string name)
        {
            lock (lockObject)
            {
                using (SqlConnection connection = new SqlConnection("your_connection_string")) // Write your connection string here
                {
                    connection.Open();
                    SqlCommand command = new SqlCommand("UPDATE Customers SET Name = @Name WHERE CustomerID = @CustomerID", connection);
                    command.Parameters.AddWithValue("@CustomerID", customerId);
                    command.Parameters.AddWithValue("@Name", name);
                    command.ExecuteNonQuery();
                }
                // The lock will be released in this area.
            }
        }
    }
}

Customer Information

Explanation of the above diagrammatic flow.

1. A new CustomerInformation Database instance is instantiated

Three threads (t1, t2, t3) are initialized to execute read and write operations on the CustomerInformation database.

2. Thread Initialization

Threads t1, t2, and t3 are launched simultaneously.

3. Thread Tasks

  • Thread t1 executes ReadCustomerDetails.
  • Thread t2 executes WriteCustomerDetails.
  • Thread t3 executes ReadCustomerDetails.

4. Locking Mechanism

  • Upon entering the ReadCustomerDetails method, t1 obtains the lockObject.
  • If t2 or t3 attempt to enter the WriteCustomerDetails or ReadCustomerDetails method while t1 holds the lock, they are blocked until the lock is released.

5. Database Interactions

  • Thread t1 reads the customerinformation.
  • Thread t2 waits until t1 releases the lock.
  • Thread t3 waits until t1 releases the lock.

6. Lock Release

  • Upon completing its read operation, t1 releases the lock.
  • Thread t2 acquires the lock and modifies the CustomerInformation details.
  • Thread t3 waits until t2 releases the lock.

7. Task Completion

  • After t2 finishes its write operation, it releases the lock.
  • Thread t3 acquires the lock and reads the customer information.

Inter-process synchronization

Inter-process synchronization is the management of shared resource access among multiple processes. It is essential for coordinating access to common resources like files, shared memory, or databases. Methods for inter-process synchronization encompass Mutex, named Semaphore, MemoryMappedFile, and database locks.

Example Scenario 1

In a situation where several applications (processes) are required to record messages in a common log file.

internal class SharedLogWriter
{
    private static readonly Mutex mutex = new Mutex(false, "Omatrixtech\\SharedLogMutex"); // Write your resource path

    public void WriteSharedLog(string message)
    {
        mutex.WaitOne();
        try
        {
            using (StreamWriter writer = new StreamWriter("sharedFile_log.txt", true))
            {
                writer.WriteLine(message);
            }
        }
        finally
        {
            mutex.ReleaseMutex(); // Mutex will be released here
        }
    }
}

Example Scenario 2

In this situation, there exists a customer information database in which various processes (beyond just threads within a singular process) have the capability to execute write operations. A named Mutex is employed to guarantee synchronization of these operations among processes, thereby preventing race conditions and upholding data integrity.

private class CustomerInformation
{
    private static readonly Mutex mutex = new Mutex(false, "Omatrixtech\\CustomerInformationMutex"); // Provide the directory path to your resource folder.

    public void WriteCustomerDetails(int customerId, string name)
    {
        mutex.WaitOne();
        try
        {
            using (SqlConnection connection = new SqlConnection("your_connection_string"))
            {
                connection.Open();
                SqlCommand command = new SqlCommand("UPDATE Customers SET Name = @Name WHERE CustomerID = @CustomerID", connection);
                command.Parameters.AddWithValue("@CustomerID", customerId);
                command.Parameters.AddWithValue("@Name", name);
                command.ExecuteNonQuery();
            }
        }
        finally
        {
            mutex.ReleaseMutex(); // Mutex is released here
        }
    }
}

Information

Detailed Diagram Explanation

  1. CustomerInformation Class
    • mutex: A named Mutex utilized for inter-process synchronization. The specific name "Omatrixtech\\CustomerInformationMutex" guarantees that the mutex can be accessed across various processes.
    • WriteCustomerDetails(): A function that modifies customer information in the database. It ensures that the process is synchronized using the mutex.
  2. Processes (P1, P2, P3): Each process represents a distinct user executing write operations on the database.
    • (p1) initiates WriteCustomerDetails().
    • (p2) initiates WriteCustomerDetails().
    • (p3) initiates WriteCustomerDetails().
  3. Synchronization
    • Process (p1) enters the WriteCustomerDetails method and invokes mutex. WaitOne() to obtain the mutex.
    • Process (p2) and Process (p3) wait until the mutex is released.
    • Upon completion of Process (p1) operation, mutex.ReleaseMutex() is called to release the mutex.
    • Process(p2) then acquires the mutex and carries out its update operation.
    • Process(p3) waits until Process (p2) releases the mutex.
    • After Process(p2) completes its operation, mutex.ReleaseMutex() is called to release the mutex.
    • Process User3 (p3) then acquires the mutex and performs its update operation.
    • After Process (p3) completes its operation, it calls mutex.ReleaseMutex() to release the mutex.

Comparative findings

  • Intra-process synchronization guarantees secure access to shared resources within a single process, usually employing tools such as lock, Monitor, Mutex, Semaphore, SemaphoreSlim, and ReaderWriterLockSlim.
  • Inter-process synchronization guarantees secure access to shared resources among multiple processes, utilizing tools such as named Mutex, Semaphore, and MemoryMappedFile.


Similar Articles