2016-08-16 230 views
1

我在写关键区域的应用程序。AutoResetEvent.WaitOne()导致死锁

我决定使用AutoResetEvent来实现互斥。 下面的代码

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"); 

      } 
     } 

    } 

我有一个按钮,其单击事件调用Button_Click()方法

它正常工作。但是,如果我在第一次致电Button_Click()完成之前再次点击该按钮的时间足够快,则整个应用程序将停止响应。

在调试窗口,我觉得这样的事情

Entering Button_Click 
Entering Button_Click 

貌似方法永远不会完成。

我挣扎了一下,发现如果我改变lock.WaitOne();

if (!sync.WaitOne(TimeSpan.FromSeconds(1))) 
    { 
     return; 
    } 

在这种情况下,我的应用程序能够避免僵局,但我不知道为什么它的工作原理。

我只知道我的操作系统课程中的IPC和C#中的asyncawait模式,我对.NET中的线程并不熟悉。

我真的很想了解幕后的真实情况。 感谢您的回信;),直到你调用AutoResetEvent.Set(),你似乎永远只是WaitOne()呼叫做

+1

张贴的片段不足以证明僵局。但是,你做错了是显而易见的,关注类名。它是自动复位*事件*。事件用于发信号,“关键区域”需要互相排斥。这需要一个互斥锁,最容易在C#中用'lock'关键字完成。在UI线程上使用锁定在形式上是非法的,实际上很可能导致死锁。 –

回答

4

你有一个僵局,因为WaitOne阻止主线程(按一下按钮处理程序是在主线程上执行),同时呼吁await当你已经不叫ConfigureAwait(false),这意味着它试图运行代码在主线程上是await之后,即使它被阻塞,这也会导致死锁。

我建议您阅读this post,详细解释死锁情况。

为您的代码,我建议把锁越深,大概是异步任务中,并尝试使用锁定,优选地,lock statement更合适的模式,因为使用Event对象是尴尬的相互排斥,因为汉斯在评论中表示。

+0

谢谢,这些帖子真的帮了很多。另一个问题,我的关键区域实际上包含一些'await'关键字。那么使用'SemaphoreSlim.WaitAsync()'怎么样? – Hohenheim

+0

看来'SemaphoreSlim'确实是一个优选的方法,具有异步锁定功能。 – argaz

0

AutoResetEvent.WaitOne()将阻止无限。

引述AutoResetEvent.WaitOne()文档:

阻塞当前线程,直到当前的WaitHandle接收信号

+0

你是对的,但是他描述的是一个更多的种族条件僵局,而不是100%,所以我认为我的解释在这种情况下更合适。 – argaz