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.
lockmechanism is lightweight. Are you sure you need to roll-your-own?Volatile.WriteuseInterlocked.Exchange(ref m_isRunning, 0);