Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does using volatile produce a different result?

Tags:

c#

    private const int Total = 500000;
    private static volatile int _count = 0;

    private static void Main()
    {
        Task task = Task.Factory.StartNew(Decrement);
        for (int i = 0; i < Total; i++)
        {
            _count++;
        }

        task.Wait();
        Console.WriteLine(_count);

        Console.ReadLine();
    }

    private static void Decrement()
    {
        for (int i = 0; i < Total; i++)
        {
            _count--;
        }
    }

Sometimes the result is 0,sometimes the result is -xxxxxx. I don't know why. Can anybody explain it and tell me the correct usage.

like image 689
Scarface Avatar asked Dec 11 '12 06:12

Scarface


People also ask

Does volatile prevent race condition?

Making a variable volatile does not protect it from race conditions, it just indicates that the object to which the variable is bound may mutate to prevent compiler optimizations, it is not needed in your case because I & j are modified in the same thread (the loop thread).

Is volatile variable thread safe?

Unlike synchronized methods or blocks, it does not make other threads wait while one thread is working on a critical section. Therefore, the volatile keyword does not provide thread safety when non-atomic operations or composite operations are performed on shared variables.

What does volatile do c#?

The volatile keyword in C# is used to inform the JIT compiler that the value of the variable should never be cached because it might be changed by the operating system, the hardware, or a concurrently executing thread.

Is volatile enough?

volatile is Not Always Enough In fact, multiple threads could even be writing to a shared volatile variable, and still have the correct value stored in main memory, if the new value written to the variable does not depend on its previous value.


2 Answers

volatile guarantees no reordering of operations and disable caching optimizations, but does not guarantees thread safety - so you can get any result from 0 to -Total (if every pair of -- and ++ are properly mixed). I've covered "preper mixing" in answer to Why the following C# multi-threaded code does not output zero though it does in debugger?.

volatile is useful when you expect someone else to modify value so your code always reads reasonably recent value, which also will be consistent (i.e. for ++ you'll not get int consisting of high part of 0xffff and low part of (0xffff+1) - you'll get either one or another) since volatile is only applicable to types that are writen atomically.

If you want to modify counter in 2 threads - use Interlocked.Increment/Interlocked.Decrement.

like image 83
Alexei Levenkov Avatar answered Oct 07 '22 00:10

Alexei Levenkov


volatile does not guarantee thread-safety as it was stated in Alexei Levenkov's answer. You can use System.Threading.Interlocked.

Your example will look like something like this after that:

    private const int Total = 500000;
    private static volatile int _count = 0;

    private static void Main()
    {
        Task task = Task.Factory.StartNew(Decrement);
        for (int i = 0; i < Total; i++)
        {
            System.Threading.Interlocked.Increment(ref _count);
        }

        task.Wait();
        Console.WriteLine(_count);

        Console.ReadLine();
    }

    private static void Decrement()
    {
        for (int i = 0; i < Total; i++)
        {
            System.Threading.Interlocked.Decrement(ref _count);
        }
    }
like image 30
Leri Avatar answered Oct 07 '22 02:10

Leri