BackgroundWorker和ConcurrentQueue

时间:2014-02-21 08:15:57

标签: c# queue backgroundworker filesystemwatcher concurrent-queue

我有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();
  }

}

2 个答案:

答案 0 :(得分:2)

是的,您可能遇到Execute问题。它可以使file未处理worker

您可以通过两种方式解决问题:
1)处理完所有排队的文件后,worker未完成。它等待AutoResetEvent以便处理下一个文件。在这种情况下,Execute应通过致电worker通知AutoResetEvent.Set 例如:

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,因为它正忙,那么worker将在BackgroundWorker.RunWorkerCompleted中再次启动,待处理的file将会处理。

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

注意:如果您决定在非GUI应用程序中使用BackgroundWorker.RunWorkerCompleted,那么您应该在Start中小心,因为BackgroundWorker.RunWorkerCompleted可以不在您调用Execute的线程和Start中将出现竞争条件。更多信息:BackgroundWorker.RunWorkerCompleted and threading

如果您同时从两个不同的主题调用Start(),那么他们都可以看到worker.IsBusy == false并且他们都会调用worker.RunWorkerAsync()。调用worker.RunWorkerAsync()稍晚于另一个线程的线程将抛出InvalidOperationException。因此,您应该捕获该异常或将IsBusy + RunWorkerAsync包装到具有锁定的关键部分,以避免竞争条件和抛出异常。

答案 1 :(得分:1)

为了不必担心这个问题,当队列为空并且在工作人员退出之前调用Start时,您可以尝试完全不离开worker方法:

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

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

其他可能性,如果没有优雅的睡眠,将使用ManualResetEvent类来表示队列为空时停止为空。