1
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Threading;
namespace JUSTFORPRACTICE
{
class Program
{
    static int total = 0;
    public static void Main()
    {
        Thread t1 = new Thread(fun);
        t1.Start();
        Thread t2 = new Thread(fun);
        t2.Start();
        Thread t3 = new Thread(fun);
        t3.Start();
        t1.Join();
        t2.Join();
        t3.Join();
        Console.WriteLine(total);
    }
    public static void fun()
    {
        for (int i = 1; i <= 10000; i++)
            total++;
    }
}

}

I've read that using Join() we can prevent the main thread from executing the futher statements until the current thread in running.But here each time when I am running i m getting different output.....Why?

METALHEAD
  • 2,734
  • 3
  • 22
  • 37
  • 3
    Because `total++` is not atomic. Try the same with `Interlocked.Increment(ref total);` – Me.Name Jul 01 '15 at 12:52
  • Bad things happen when multiple threads access the same resource at the same time, in your case the `total` variable. You could take a look at this https://stackoverflow.com/questions/154551/volatile-vs-interlocked-vs-lock for some answers. – Dirk Jul 01 '15 at 12:52
  • The main thread does block but you've started three other threads that modify `total` in an unsafe way – Panagiotis Kanavos Jul 01 '15 at 12:52
  • `total` isn't atomic and therefore not thread safe. What if `t1` reads the value as 5, tries to update it to 6 where `t2` has also read this as 5? – Charles Mager Jul 01 '15 at 12:52
  • Dude....do you mean that `Interlocked.Increment(ref total)` allows only one thread to act at a time....? – METALHEAD Jul 01 '15 at 12:59

1 Answers1

4

You have three threads running concurrently, all mutating shared state in an unsafe way:

  • The increment isn't atomic, in that it's "read, locally increment, write" - if multiple threads read the same value, then each locally increment, then write, you'll effectively "lose" all but one of those increments.
  • There are no memory barriers to force each thread to read the most recent value of the variable from main memory or write the result back to main memory immediately. Each thread could execute fun as "read from main memory once into a register; increment the register 10001 times; write the result back into main memory"...

If you change total++ to Interlocked.Increment(ref total) then each increment operation will be atomic and is guaranteed to work on fresh data, with the results being visible to all threads immediately.

Moral of the question: be incredibly careful when working with mutable shared data from multiple threads.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Dude....do you mean that `Interlocked.Increment(ref total)` allows only one thread to act at a time....? – METALHEAD Jul 01 '15 at 12:58
  • 1
    @METALHEAD: Well, only one thread is able to perform the increment step at a time, yes - and the result will be immediately available to all other threads. You've still got the looping part happening in three separate threads though. (And the increment operation is blindingly quick, obviously.) – Jon Skeet Jul 01 '15 at 12:59
  • For second part (to avoid caching value due to compiler optimization) you can mark `total` as `volatile`. – Sinatr Jul 01 '15 at 13:01
  • @Sinatr: Well, `volatile` has some *very* specific and often-poorly-understood semantics. I think it's very, very rarely the right solution - I view `volatile` as an "experts only" keyword (and I don't classify myself as expert enough to use it correctly). – Jon Skeet Jul 01 '15 at 13:03
  • @JonSkeet Sir, where do you bring so much humility from? – aditya_medhe May 26 '17 at 18:19