Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locking on a primitive type

I want to check some locking behaviors and i can't understand this :

static void Main(string[] args)
{
    for (int i = 0; i < 10; i++)
    {
        Task.Factory.StartNew(() =>
        {
            MultithreadedMethod();
        });
    }

    Thread.Sleep(2000);
    Console.WriteLine(count);
}

static int count = 0;
private static readonly int sync = 5;

public static void MultithreadedMethod()
{
    if (Monitor.TryEnter(sync))
    {
        count++;
        Monitor.Exit(sync);
    }
}

I thought that this should not work due to the fact i am doing the synchronization on an integer value. First Boxing, then Unboxing and i should get a System.Threading.SynchronizationLockException because of the missing sync block root ( i know this is specific to reference types ). I am not going to fool myself, even if this works for a few iterations, it's not really synchronized.. so, taking into consideration the non atomic property of the increment operation .. i will not get deterministic results.. i am aware of this.

Indeed, when i get rid of that Thead.Sleep and put a Wait on the Task.. the exception comes into place.

Task.Factory.StartNew(() =>
 {
       MultithreadedMethod();
 }).Wait();

I think an exception should be thrown here : Monitor.Exit(sync)

but what catches it?

Update 1: pic added.

enter image description here

like image 926
Olaru Mircea Avatar asked Oct 13 '15 08:10

Olaru Mircea


1 Answers

but what catches it?

The exception thrown inside a Task object is implicitly captured inside that Task. Unless you access the Task.Exception or Task.Wait/Task.Result properties, or await on the returned task, the exception would be swallowed and you won't be able to see it. That is why using Wait propagates the exception and you can see it in the console. Same will happen if you use Task.WaitAll to wait on all the tasks to finish.

If you don't use any of these, you can still see the exception by registering to TaskScheduler.UnobservedTaskException:

static void Main(string[] args)
{
    TaskScheduler.UnobservedTaskException += (s,e) => Console.WriteLine(e.Exception);

    for (int i = 0; i < 10; i++)
    {
        Task.Factory.StartNew(() =>
        {
            MultithreadedMethod();
        });
    }

    Thread.Sleep(2000);
    Console.WriteLine(count);
}

Yields:

Unobserved task exception

Note this code still has race-conditions due to the fact we're not actually waiting for any of the results to come, which may not happen after a sleep of 2 seconds.

like image 171
Yuval Itzchakov Avatar answered Sep 29 '22 05:09

Yuval Itzchakov