簡體   English   中英

使用 manualResetEvent 死鎖 - c#

[英]Deadlock using manualResetEvent - c#

在我的示例 UI 中,有 3 個按鈕。 開始按鈕,暫停,恢復。 開始按鈕是 async/await 並且會調用一個方法:

private async void btnStart_Click()
{
    await Task.Run(() => StartingMethod());
}

我們有一個名為_pauseEvent的全局ManualResetEvent 這是StartingMethod()的結構:

public void StartingMethod()
{
   for (int i =0; i < 1000000; i++)
   {
      _pauseEvent.WaitOne();
      // Do Something
   }
}

這是暫停/恢復按鈕:

private async void btnPause_Click()
{
    await Task.Run(() => _pauseEvent.Reset());
}

private async void btnResume_Click()
{
    await Task.Run(() => _pauseEvent.Set());
}

但有時它會給暫停/恢復調用時的ManualResetEvent The handle is invalid 這似乎是一個競爭條件問題。 所以我決定創建一個全局鎖對象。

Object _lockObject = new object();

這是新的:

public void StartingMethod()
{
   for (int i =0; i < 1000000; i++)
   {
      lock (_lockObject)
         _pauseEvent.WaitOne();
      // Do Something
   }
}

private async void btnPause_Click()
{
    await Task.Run(() => lock (_lockObject) _pauseEvent.Reset());
}

private async void btnResume_Click()
{
    await Task.Run(() => lock (_lockObject) _pauseEvent.Set());
}

但似乎,在這里,我將再次面臨鎖定對象的死鎖問題。 我該如何處理這種情況?

我很確定使用async voidTask.Runlock會導致死鎖。 我建議做的是使用CancellationToken和一些中間狀態來表示StartingMethod的進度。

請注意,它不會處理許多StartingMethod的並發執行,您需要使用CanExecute類的CanExecute來防止用戶連續多次單擊開始按鈕。

private int _state = 0;

public void StartingMethod(CancellationToken cancellation, bool resetState)
{
   if (resetState)
   {
       _state = 0;
      // reset your additional data  
   }
   for (int i = _state; i < 1000000; i++)
   {
      if (!cancellation.IsCancellationRequested)
      { 
          // Recover saved state
          // Do Something
      }
      else
      {
          // save intermediate state
          // save your additional data
          _state = i;
      }
   }
}

我強烈建議將 try-catch 添加到async方法中:

private CancellationTokenSource _cts;

private async Task HandleStart(bool resetState)
{ 
   _cts = new CancellationTokenSource();
    try
    {
        await Task.Run(() => StartingMethod(_cts.Token, resetState));
    }
    catch (Exception e)
    {
    // log or show some error on UI
    }
    finally
    {
        _cts.Dispose();
    }
}

private void btnStart_Click()
{
    // No need to await because it's a single statement in a method
    HandleStart(true);
}

private async void btnPause_Click()
{
    _cts.Cancel();
}

private void btnResume_Click()
{
    HandleStart(false);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM