Lockless multithreading of an integer

167 views Asked by At

Given a scenario where there's a function that should only be executed by one thread at any given time, and the rest just return (since a specific state is already being worked on), what's the best way to accomplish this?

public void RunOnce()
{
    if(Interlocked.Exchange(ref m_isRunning, 1) == 1)
        return;

    // Run code that should only be executed once
    
    // What mechanism do we use here to ensure thread safety?
    Volatile.Write(ref m_isRunning, 0);
}

Would the same mechanism apply if m_isRunning is a state (ie. an integer representing an enum)?

1

There are 1 answers

9
Theodor Zoulias On BEST ANSWER

The code in your question is thread-safe IMHO, but in general the Interlocked.CompareExchange method is more flexible than the Interlocked.Exchange for implementing lock-free multithreading. Here is how I would prefer to code the RunOnce method:

int _lock; // 0: not acquired, 1: acquired

public void RunOnce()
{
    bool lockTaken = Interlocked.CompareExchange(ref _lock, 1, 0) == 0;
    if (!lockTaken) return;
    try
    {
        // Run code that should be executed by one thread only.
    }
    finally
    {
        bool lockReleased = Interlocked.CompareExchange(ref _lock, 0, 1) == 1;
        if (!lockReleased)
            throw new InvalidOperationException("Could not release the lock.");
    }
}

My suggestion though would be to use the Monitor class:

object _locker = new();

public void RunOnce()
{
    bool lockTaken = Monitor.TryEnter(_locker);
    if (!lockTaken) return;
    try
    {
        // Run code that should be executed by one thread only.
    }
    finally { Monitor.Exit(_locker); }
}

...or the SemaphoreSlim class if you prefer to prevent reentrancy:

SemaphoreSlim _semaphore = new(1, 1);

public void RunOnce()
{
    bool lockTaken = _semaphore.Wait(0);
    if (!lockTaken) return;
    try
    {
        // Run code that should be executed by one thread only.
    }
    finally { _semaphore.Release(); }
}

It makes the intentions of your code cleaner IMHO.