This is an advanced question in C# multithreading.
Let's say I have this code that is used as a locking mechanism to enable only one thread to start some operation:
private static int _guard = 0;
private static bool acquire() {
return Interlocked.CompareExchange(ref _guard, 1, 0) == 0;
}
private static void release() {
Volatile.Write(ref _guard, 0);
}
This lock is used to protect method that should be execute only by one thread at the time:
public readonly Status Status = new (); // updated from thread that runs someTask
void TryRunningTask {
if (acquire()) {
return await someTask();
} else {
InfoMessage = "Another user is currently running someTask.";
}
}
My question is, if I change release()
as following:
private static void release() {
_guard = 0;
}
Would the program still behave completely the same? Would that break the thread-safety? Would this change make any sense?
The reasoning behind my idea for this change is following:
- there are no other read/write operations inside of
release()
method. In MS Docs for Volatile.Write method it says:
Writes a value to a field. On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: If a read or write appears before this method in the code, the processor cannot move it after this method.
So because my release()
method has no other operations before/after Volatile.Write()
call I guess I can just replace it with simple assignment statement _guard = 0;
right?
- as per C# Standard section 10.6 the
_guard = 0;
operation is guaranteed to be atomic operation:
10.6 Atomicity of variable references
Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types.
SemaphoreSlim
?bool Acquire() => semaphore.Wait(0);
,void Release() => semaphore.Release();
Using a built-in synchronization primitive should be safer than inventing your own. – EroseSemaphoreSlim
. – Fore