Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Monitor.Enter to lock variable incremetation

in the following code example:

class Program
{
    private static int counter = 0;
    public static object lockRef = new object();

    static void Main(string[] args)
    {
        var th = new Thread(new ThreadStart(() => {
            Thread.Sleep(1000);
            while (true)
            {
                Monitor.Enter(Program.lockRef);
                ++Program.counter;
                Monitor.Exit(Program.lockRef);
            }
        }));
        th.Start();

        while (true)
        {
            Monitor.Enter(Program.lockRef);
            if (Program.counter != 100)
            {
                Console.WriteLine(Program.counter);
            }
            else
            {
                break;
            }
            Monitor.Exit(Program.lockRef);
        }
        Console.Read();
    }
}

Why does the while loop inside Main function does not break even if I use lock with Monitor? If I add Thread.Sleep(1) inside the Thread while everything works as expected and even without Monitor…

Is it just happening too fast that the Monitor class doesn't have enough time to lock?

NOTE: The != operator is intended. I know I can set it to < and solve the problem. What I was trying to achieve is to see it working with Monitor class and not working without it. Unfortunately it doesn't work both ways. Thanks

like image 668
Dimkin Avatar asked Dec 26 '22 21:12

Dimkin


2 Answers

The first thread with the while, might get scheduled twice in a row (ie the Monitor might not be fair.)

See this related question: Does lock() guarantee acquired in order requested?

like image 178
Daniel James Bryars Avatar answered Jan 01 '23 10:01

Daniel James Bryars


Let's assume you have 1 CPU available. This is how the execution will look like

T1        [SLEEP][INCREMENT][SLEEP][INCREMENT][SLEEP][INCREMENT][SLEEP]
T2   --[L][CK][UL][L][CK][UL][L][CK][UL][L][CK][UL][L][CK][UL][L][CK][UL]
CPU1 [  T2  ][T1][  T2  ][  T1  ][  T2  ][T1][  T2  ][  T1  ][  T2  ][T1]...

Where:

T1 is th thread
T2 is main thread
[L][CK][UL] is lock, check, unlock - the workload of the main thread
CPU1 is task scheduling for the CPU

Note a short [T1] is a call to Thread.Sleep. This results in the current thread yielding control immediately. This thread will not be scheduled for executing for time greater or equal to the specified milisecond parameter.

Longer [ T1 ] is where increment in while loop happens.

Important: T1 will not execute a single increment and then switch to another thread. This is where the problem. It will do many iterations until the current thread execution quant expires. On average you can think of execution quant ~ 10-30 mili seconds.

This is exactly supported by the output, which on my machine was

0
0
0
...
56283
56283
56283
...
699482
699482
699482
...
like image 34
oleksii Avatar answered Jan 01 '23 09:01

oleksii