Monitor.TryEnter不起作用

时间:2014-01-28 15:49:19

标签: c# wpf multithreading

我的代码隐藏的一部分:

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

输出(在不到5秒的时间内按下几次):

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

如何先得??永远不会采用_sync锁,为什么?

5 个答案:

答案 0 :(得分:14)

混合Monitorawait ......有点风险。看起来你要做的就是确保它一次只运行一次。我怀疑Interlocked可能更简单:

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

注意您可能还想想如果发生错误等会发生什么;该值如何重置?您可以使用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);
}

答案 1 :(得分:12)

您不能将Monitorawait一起使用线程仿射类型。在这种特殊情况下,你总是在同一个线程(UI线程)上获取锁,这种类型的锁允许递归锁定。

请改为SemaphoreSlimWaitAsyncRelease而不是EnterExit):

SemaphoreSlim _sync = new SemaphoreSlim(1);

private async void OnKeyDown(object sender, KeyEventArgs e) {
  await _sync.WaitAsync();

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

  _sync.Release();
}

答案 2 :(得分:6)

您无法在awaitMonitor.TryEnter()方法调用之间使用Monitor.Exit()。在await之后,线程上下文可能不同,这意味着线程不会有entered锁,因此它不能exit它。

事实上,如果您使用lock关键字,编译器会保护您:

lock(_sync)
{
  await Task.Delay(...); // <- Compiler error...
}

答案 3 :(得分:2)

如果当前主题已获得锁定,TryEnter会成功。 KeyDown事件将始终在Dispatcher线程上触发,而后台线程正在处理等待,然后在解调器线程上将解锁队列排队。

答案 4 :(得分:2)

TryEnter将在你的gui线程上运行。它对于一个线程有效地多次获取监视器而不会阻塞是有效的,它只需要释放它们相同的次数。

您对Monitor.Exit的来电将在async来电指示的上下文中运行。如果它最终在调用TryEnter的线程以外的线程上运行,那么它将无法释放监视器。

所以,你每次都在同一个线程上获取监视器,它永远不会阻塞,你可以在其他一些线程上释放它,这可能有用。这就是为什么你能够在5秒窗口内快速点击。