SoFunction
Updated on 2025-04-13

Summary of five methods to realize reading and writing separation in C#

1. Use ReaderWriterLockSlim

.NET provides high-performance read and write locks that support the following modes:

  • Read mode (Read Lock): Allow multiple threads to read at the same time.

  • Write mode (Write Lock): Exclusive lock, all other read and write operations will be blocked.

using ;
 
public class ReadWriteExample
{
    private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
    private int _sharedData = 0;
 
    // Read operation    public int ReadData()
    {
        _lock.EnterReadLock();
        try
        {
            return _sharedData;
        }
        finally
        {
            _lock.ExitReadLock();
        }
    }
 
    // Write operation    public void WriteData(int value)
    {
        _lock.EnterWriteLock();
        try
        {
            _sharedData = value;
        }
        finally
        {
            _lock.ExitWriteLock();
        }
    }
}

advantage

  • Fine-grained control of read and write operations.

  • Supports recursive locking and timeout mechanisms.

shortcoming

  • The acquisition and release of locks need to be manually managed.

2. Use Concurrent concurrent collection

.NET'sThe namespace provides thread-safe collection classes, and read-write separation logic has been implemented internally:

  • ConcurrentDictionary<TKey, TValue>

  • ConcurrentQueue<T>

  • ConcurrentBag<T>

using ;
 
public class ConcurrentExample
{
    private readonly ConcurrentDictionary&lt;string, int&gt; _data = new();
 
    // Read operation (no lock)    public int GetValue(string key)
    {
        return _data.TryGetValue(key, out int value) ? value : -1;
    }
 
    // Write operation (using fine-grained lock internally)    public void UpdateValue(string key, int value)
    {
        _data.AddOrUpdate(key, value, (k, oldValue) =&gt; value);
    }
}

advantage

  • Works out of the box without manually managing locks.

  • High performance, suitable for high-frequency reading and writing scenarios.

shortcoming

  • Low flexibility and only applies to predefined collection types.

3. Based on volatile and memory barriers (Memory Barrier)

Suitable for the read and write separation of simple variables, throughvolatileKeywords ensure memory visibility:

public class VolatileExample
{
    private volatile bool _flag = false;
    private int _data = 0;
 
    // Read operation (no lock)    public int ReadData()
    {
        if (_flag)
            return _data;
        return -1;
    }
 
    // Write operation (need to synchronize)    public void WriteData(int value)
    {
        (ref _data, value); // Atomic writing        (ref _flag, true);        // Make sure that the write is visible to other threads    }
}

advantage

  • Lightweight, suitable for simple scenarios.

  • Lockless read operation.

shortcoming

  • Applicable only to simple types (e.g.intbool)。

  • Memory visibility needs to be handled manually.

4. Database read and write separation

At the database level, the read and write separation (such as the master-slave architecture), the C# code routes requests through different connection strings:

public class DatabaseRouter
{
    private static readonly string ReadConnectionString = "Server=ReadServer;...";
    private static readonly string WriteConnectionString = "Server=WriteServer;...";
 
    public IDbConnection GetReadConnection() =&gt; 
        new SqlConnection(ReadConnectionString);
 
    public IDbConnection GetWriteConnection() =&gt; 
        new SqlConnection(WriteConnectionString);
}
 
// Use exampleusing (var readConn = ())
{
    // Perform query operations}
 
using (var writeConn = ())
{
    // Perform update operation}

advantage

  • Directly utilize the database master-slave replication feature.

  • Reduce the pressure on the main warehouse.

shortcoming

  • The database needs to support master-slave synchronization.

  • There may be a data synchronization delay.

5. Based on immutable data (Immutable Data)

Lockless reading is achieved by generating new objects every time (such as usingImmutableList<T>Or custom immutable types):

using ;
 
public class ImmutableExample
{
    private ImmutableList&lt;int&gt; _data = ImmutableList&lt;int&gt;.Empty;
 
    // Read operation (no lock)    public int GetItem(int index)
    {
        return _data[index];
    }
 
    // Write operation (atomic replacement)    public void AddItem(int item)
    {
        (ref _data, list =&gt; (item));
    }
}

advantage

  • Completely lock-free reading.

  • Natural thread-safe.

shortcoming

  • Frequent modifications may cause memory pressure.

Select a policy

Scene Recommended plan
Fine-grained code-level read and write control ReaderWriterLockSlim
High frequency concurrent set operation ConcurrentDictionarySuch concurrent collection
Reading and writing simple variables volatile + Interlocked
Database access separation Master-slave architecture + connection routing
High frequency reading and low frequency writing Immutable data (such asImmutableList

Things to note

  • Avoid deadlocksEnsure that the acquisition and release of the lock appears in pairs (usingtry-finallypiece).

  • Performance trade-offs: Read and write locks are suitable for scenarios that read more and write less. When writing frequently, they may not be as efficient as ordinary locks.

  • Data consistency: The master-slave synchronization delay problem needs to be dealt with when the database is separated.

This is the end of this article about the five methods of C#’s reading and writing separation. For more related C# reading and writing separation, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!