簡體   English   中英

BackgroundWorker和ConcurrentQueue

[英]BackgroundWorker and ConcurrentQueue

我有一個FileSystemWatcher ,它正在尋找新文件,將文件名放在一個Queue 在一個單獨的線程中,隊列被解決了。 我的代碼正在運行,但我懷疑是否會因為異步過程而丟失信息。 請注意評論中解釋的代碼:(我想也許我需要某個地方的線程鎖?)(代碼簡化)

public class FileOperatorAsync
{
  private ConcurrentQueue<string> fileQueue;
  private BackgroundWorker worker;
  private string inputPath;

  public FileOperatorAsync(string inputPath)
  {
     this.inputPath = inputPath;
     fileQueue = new ConcurrentQueue<string>();
     worker = new BackgroundWorker();
     worker.WorkerSupportsCancellation = true;
     worker.DoWork += worker_DoWork;
     Start();
  }

  void worker_DoWork(object sender, DoWorkEventArgs e)
  {
     try
     {
        string file;
        while (!worker.CancellationPending && fileQueue.TryDequeue(out file)) //As long as queue has files
        {
          //Do hard work with file
        }
        //Thread lock here?
        //If now Filenames get queued (Method Execute -> Worker is still busy), they wont get recognized.. or?
     }
     catch (Exception ex)
     {
        //Logging
     }
     finally
     {
        e.Cancel = true;
     }
  }

  public void Execute(string file) //called by the FileSystemWatcher
  {
     fileQueue.Enqueue(file);
     Start(); //Start only if worker is not busy
  }

  public void Start()
  {
     if (!worker.IsBusy)
        worker.RunWorkerAsync();
  }

  public void Stop()
  {
     worker.CancelAsync();
  }

}

是的,您可能遇到Execute問題。 它可以保留您的worker未處理的file

您可以通過兩種方式解決它:
1)處理完所有排隊的文件后,您的worker沒有完成。 它等待AutoResetEvent以處理下一個文件。 在這種情況下, Execute應通過調用AutoResetEvent.Set通知worker
例:

AutoResetEvent event;
...
// in worker_DoWork
while(!worker.CancellationPending){
    event.WaitOne();
    // Dequeue and process all queued files
}

...
// in Execute
fileQueue.Enqueue(file);
event.Set();

2)您的工作人員在處理完所有排隊的文件后完成(就像您現在一樣)但您可以在BackgroundWorker.RunWorkerCompleted檢查是否還有要處理的文件並再次運行該工作程序。
在這種情況下,如果Execute尚未啟動worker因為它正忙,那么將在BackgroundWorker.RunWorkerCompleted再次啟動worker ,並處理掛起的file

// in  worker_RunWorkerCompleted
if (!fileQueue.IsEmpty())
    Start();

注意 :如果您決定在非GUI應用程序中使用BackgroundWorker.RunWorkerCompleted ,那么在Start應該小心,因為可以在您調用Execute的線程上調用BackgroundWorker.RunWorkerCompleted ,並且Start中將出現爭用條件。 更多信息: BackgroundWorker.RunWorkerCompleted和threading

如果你同時從兩個不同的線程調用Start() ,那么他們都可以看到worker.IsBusy == false ,他們都會調用worker.RunWorkerAsync() 調用worker.RunWorkerAsync()的線程比另一個線程晚一點將拋出InvalidOperationException 因此,您應該捕獲該異常或將IsBusy + RunWorkerAsync包裝到具有鎖定的關鍵部分,以避免競爭條件和異常拋出。

為了不必擔心這個問題,當隊列為空並且在工作人員退出之前調用Start ,您可以嘗試完全不離開worker方法:

while (!worker.CancellationPending)
{
    while (!worker.CancellationPending && !fileQueue.TryDequeue(out file))
    {
        Thread.Sleep(2000);
    }

    if (worker.CancellationPending)
    {
        break;
    }
    //
}

其他可能性,如果沒有優雅的睡眠,將使用ManualResetEvent類來指示何時隊列為空並且停止為空。

暫無
暫無

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

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