Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

thread-safety of primitive concurrent read and write

  • Simplified illustration below, how does .NET deal with such a situation?
  • and if it would cause problems, would i have to lock/gate access to each and every field/property that might at times be written to + accessed from different threads?

A field somewhere

public class CrossRoads(){
    public int _timeouts;
}

A background thread writer

public void TimeIsUp(CrossRoads crossRoads){
    crossRoads._timeouts++;
}

Possibly at the same time, trying to read elsewhere

public void HowManyTimeOuts(CrossRoads crossRoads){
    int timeOuts = crossRoads._timeouts;
}
like image 890
Cel Avatar asked Jan 07 '12 23:01

Cel


People also ask

Are concurrent reads thread-safe?

Software libraries can provide certain thread-safety guarantees. For example, concurrent reads might be guaranteed to be thread-safe, but concurrent writes might not be. Whether a program using such a library is thread-safe depends on whether it uses the library in a manner consistent with those guarantees.

Is primitive data types thread-safe?

Primitive types are not thread safe.

What are thread-safe methods?

When multiple threads are working on the same data, and the value of our data is changing, that scenario is not thread-safe and we will get inconsistent results. When a thread is already working on an object and preventing another thread on working on the same object, this process is called Thread-Safety.

How do I make my API thread-safe?

It's also possible to achieve thread-safety using the set of atomic classes that Java provides, including AtomicInteger, AtomicLong, AtomicBoolean and AtomicReference. Atomic classes allow us to perform atomic operations, which are thread-safe, without using synchronization.


2 Answers

The simple answer is that the above code has the ability to cause problems if accessed simultaneously from multiple threads.

The .Net framework provides two solutions: interlocking and thread synchronization.

For simple data type manipulation (i.e. ints), interlocking using the Interlocked class will work correctly and is the recommended approach.

In fact, interlocked provides specific methods (Increment and Decrement) that make this process easy:

Add an IncrementCount method to your CrossRoads class:

public void IncrementCount() {
    Interlocked.Increment(ref _timeouts);
}

Then call this from your background worker:

public void TimeIsUp(CrossRoads crossRoads){
    crossRoads.IncrementCount();
}

The reading of the value, unless of a 64-bit value on a 32-bit OS, are atomic. See the Interlocked.Read method documentation for more detail.

For class objects or more complex operations, you will need to use thread synchronization locking (lock in C# or SyncLock in VB.Net).

This is accomplished by creating a static synchronization object at the level the lock is to be applied (for example, inside your class), obtaining a lock on that object, and performing (only) the necessary operations inside that lock:

    private static object SynchronizationObject = new Object();

    public void PerformSomeCriticalWork()
    {
        lock (SynchronizationObject)
        {
            // do some critical work
        }
    }
like image 60
competent_tech Avatar answered Oct 13 '22 01:10

competent_tech


The good news is that reads and writes to ints are guaranteed to be atomic, so no torn values. However, it is not guaranteed to do a safe ++, and the read could potentially be cached in registers. There's also the issue of instruction re-ordering.

I would use:

Interlocked.Increment(ref crossroads._timeouts);

For the write, which will ensure no values are lost, and;

int timeouts = Interlocked.CompareExchange(ref crossroads._timeouts, 0, 0);

For the read, since this observes the same rules as the increment. Strictly speaking "volatile" is probably enough for the read, but it is so poorly understood that the Interlocked seems (IMO) safer. Either way, we're avoiding a lock.

like image 42
Marc Gravell Avatar answered Oct 13 '22 00:10

Marc Gravell