Reputation: 175
I'm writing a application with a critical region.
And I decide to use AutoResetEvent to achieve mutual exclusion. Here's the code
public class MyViewModel
{
private AutoResetEvent lock = new AutoResetEvent(true);
private aync Task CriticalRegion()
{
Dosomething();
}
public async Task Button_Click()
{
Debug.WriteLine("Entering Button_Click");
lock.WaitOne();
try
{
await CriticalRegion();
}
finally
{
lock.Set();
Debug.WriteLine("Leaving Button_Click");
}
}
}
I have a button whose click event calls the Button_Click()
method
It works normally. But, if I'm quick enough to click the button for another time before the first call to Button_Click()
completes, the whole app stops responding.
In the Debug window I find something like this
Entering Button_Click
Entering Button_Click
Looks like the method never completes.
I struggled a bit and find that if I change lock.WaitOne();
to
if (!sync.WaitOne(TimeSpan.FromSeconds(1)))
{
return;
}
In this case my app is able to avoid the deadlock,but I don't know why it works.
I only know about the IPC from my OS course and the async
and await
pattern in C#, and I'm not so familiar with the thread in .Net world.
I really want to understand what's really going on behind the scenes. Thanks for any replys ;)
Upvotes: 3
Views: 2298
Reputation: 1488
You have a deadlock because WaitOne
is blocking the main thread (button click handler is executed on the main thread), while you haven't called ConfigureAwait(false) when calling await
, which means that it tries to run the code which is after await
on the main thread, even if it's blocked, which would causes a deadlock.
I suggest reading this post for a thorougher explanation of the dead lock situation.
For your code, I would suggest putting the lock deeper, probably within the async Task, and trying to use a more suitable pattern for locking, preferably, the lock statement, because using Event
objects is awkward for mutual exclusion, as Hans stated in the comment.
Upvotes: 4
Reputation: 18320
AutoResetEvent.WaitOne()
will block infinitely until you call AutoResetEvent.Set()
, which you never seem to do except for after the WaitOne()
call.
Quoting the AutoResetEvent.WaitOne()
documentation:
Blocks the current thread until the current WaitHandle receives a signal.
Upvotes: 1