Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monitor.TryEnter doesn't work

Part of my code-behind:

object _sync = new object();

private async void OnKeyDown(object sender, KeyEventArgs e) {
    if (!Monitor.TryEnter(_sync)) return;

    Trace.Write("taken...");
    await Task.Delay(TimeSpan.FromSeconds(5));
    Trace.WriteLine(" done");

    Monitor.Exit(_sync);
}

Output (pressing several times in less than 5 seconds):

taken...taken...taken... done
done
done

How-come?? the _sync lock is never being taken, why?

like image 620
Tar Avatar asked Jan 28 '14 15:01

Tar


People also ask

What is Monitor TryEnter?

TryEnter(Object, TimeSpan, Boolean) Attempts, for the specified amount of time, to acquire an exclusive lock on the specified object, and atomically sets a value that indicates whether the lock was taken. TryEnter(Object, Int32, Boolean)

What is the difference between lock and monitor in C#?

CSharp Online Training Both Monitor and lock provides a mechanism that synchronizes access to objects. lock is the shortcut for Monitor. Enter with try and finally. Lock is a shortcut and it's the option for the basic usage.

Which method is used to release an exclusive lock on the specified object?

Exit() : Releases an exclusive lock on the specified object. This action also marks the end of a critical section protected by the locked object.


1 Answers

Mixing Monitor and await is... more than a little risky. It looks like what you are trying to do is to ensure it only runs once at a time. I suspect Interlocked may be simpler:

object _sync = new object();
int running = 0;
private async void OnKeyDown(object sender, KeyEventArgs e) {
    if(Interlocked.CompareExchange(ref running, 1, 0) != 0) return;

    Trace.Write("taken...");
    await Task.Delay(TimeSpan.FromSeconds(5));
    Trace.WriteLine(" done");

    Interlocked.Exchange(ref running, 0);
}

Note you might also want to think what happens if an error occurs etc; how does the value become reset? You can probably use try/finally:

if(Interlocked.CompareExchange(ref running, 1, 0) != 0) return;

try {
    Trace.Write("taken...");
    await Task.Delay(TimeSpan.FromSeconds(5));
    Trace.WriteLine(" done");
} finally {
    Interlocked.Exchange(ref running, 0);
}
like image 77
Marc Gravell Avatar answered Oct 18 '22 21:10

Marc Gravell