In a multithreaded scenario does it make sense to declare a boolean property of a class that is read and set by many threads simultaneously in the following way in C# .net?
public class MyClass
{
private long _check;
public bool Check
{
get => Interlocked.Read(ref _check) == 1;
set => Interlocked.Exchange(ref _check, Convert.ToInt64(value));
}
}
CodePudding user response:
As far as I know, that should be safe as long as all access is thru the Check
property.
Another option would be to use a regular bool-property and a lock to ensure thread safety. It should also be possible to use memoryBarriers to ensure safety, but unless performance is critical I would probably stick to a lock or the current example since they are easier to reason about. As far as I know, Interlocked.Read and Interlocked.Exchange are simply shortcuts to insert the required memory barriers, as well as to handle platforms where reading/writing 64-bit values are not atomic.
Simply marking the field as volatile might be sufficient. I'm fairly sure volatile
affords less protection than memory barriers, but I'm not well versed enough in the difference to try to explain it. I would suggest reading John Skeets article on volatility for more info.
Note that this only protects a single access to the field. If you have multiple operations that need to be done in some global order you need to have lock to protect that entire section. Or use CompareExchange to do a check and exchange as a single atomic operation.
CodePudding user response:
Your implementation of a volatile bool
field is overly complex. You can achieve exactly the same thing like this:
private bool _check;
public bool Check
{
get => Volatile.Read(ref _check);
set => Volatile.Write(ref _check, value);
}
...or even simpler like this:
public volatile bool Check;
The Interlocked.Exchange
is preferable to the Volatile.Write
only when you want to get the previous value as an atomic operation. Otherwise the Volatile.Write
has the same effect and it's more lightweight.
Whether having such a field is a good idea in the first place, depends on the scenario. If you have a loop and you want to use the field as a breaking mechanism, it might be a good idea. If you want to synchronize the access to some other shared state of your class, it's probably not.