Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are class member enums thread safe?

Take the following as an example

public class MyClass
{
      private MyEnum _sharedEnumVal { get; set; }
}

If methods within MyClass ran on different threads and read/updated _sharedEnumVal, am I right in saying that a lock, or other mechanism, would be required to keep the variable thread safe like other primitives or are enums special?

Thanks

like image 332
Fred Johnson Avatar asked Feb 01 '16 19:02

Fred Johnson


People also ask

Are enums thread-safe C#?

Well by default, enum is of type int, 32bit, unless explicitly specified otherwise. That means, that reading/writing is atomic => thread-safe.

Is enum synchronized?

methods in enum are not thread-safe until you make it synchronized.

Can enums be strings?

No they cannot. They are limited to numeric values of the underlying enum type.

Are vectors thread-safe?

Vector is a thread-safe collection - all its methods are synchronized by default. This is why it's recommended to use ArrayList instead - it's not thread-safe which results in a better performance for single-thread applications.


2 Answers

Thread-safety is a tricky subject. The updates to the enum are always atomic. So even if thousands of threads try to update the same enum at once, you will never get an invalid, half-updated enum value. The value itself will always be valid. But even when you update the enum it is never guaranteed that other threads would read the "latest" value due to cache-incoherency between multiple cores. To ensure that all cores are synchronized you would need a memory barrier.

But even that is not the guarantee of thread-safety because data races can still happen. Say you have this logic somewhere in your class:

 public void DoSomething()
 {
    if (_sharedEnumVal == MyEnum.First) {
       DoPrettyThings();
    } else {
       DoUglyThings();
    }
 }

 public void UpdateValue(MyEnum newValue)
 {
     _sharedEnumVal = newValue;
 }

and you have these two different threads:

 static MyClass threadSafeClass = new MyClass();

 void ThreadOne()
 {
    while (true) 
    {
        threadSafeClass.UpdateValue(MyEnum.Second);
        DoSomething();
    }
 }

 void ThreadTwo()
 {
    while (true)
    {
       threadSafeClass.UpdateValue(MyEnum.First);
       DoSomething();
    }
 }

Here, although the updates to the enum are atomic, two threads will be "racing" to change and use enum value to their own purposes and when DoSomething is called, there is no guarantee what value the enum would have. You would get completely unexpected results. ThreadTwo might cause pretty things and ThreadOne would cause ugly things to happen, the exact opposite of what's expected.

In that case you would still need locking to ensure thread-safety of the class behavior.

like image 138
Sedat Kapanoglu Avatar answered Oct 20 '22 15:10

Sedat Kapanoglu


I failed to understand, why this topic was downvoted:). There are some good points and some bad ideas and some even upvoted here! So let's sort the bits.

The question here is actually about atomicity. If the operation is atomic, then it is inherently thread-safe without locking for some operations like read/write and other operations allowed thanks to Interlocked class for given type.

Now, .Net is stating, that int read/write is atomic. Same for all types that fit into 32bit's, 64bit types are not atomic! read/write of the object reference is atomic too.

Some operations are atomic, some not, like increment, unless you are calling Interlocked.Increment.

Now why I talk about int? Well by default, enum is of type int, 32bit, unless explicitly specified otherwise.

That means, that reading/writing is atomic => thread-safe.

Btw, it is usually a bad idea to keep a naked property, I would rather use variable behind the property and play with the variable because it is necessary to use Interlocked methods.

There are many useful ways where atomicity is good enough guarantee to work with without locking. For example background thread status. Or a property that allowing background workers to work, until it is changed to some expected value, providing info for background workers to stop etc.

Also, Interlocked class is extending these scenarios for shared iterating variable and many more.

As Chris Hannon noted, the simple read/write can lead to the stale as data won't be updated unless specifically read/write operations would be decorated by memory barrier or Interlocked operations would be used, Interlocked.Add for reading, interlocked.CompareExchange for writing, where caches will be updated. Thanks to Chris for good point I missed!

like image 36
ipavlu Avatar answered Oct 20 '22 15:10

ipavlu