1

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

}
4

2 回答 2

2

是的,您可能对Execute. 它可能会留下file未处理的worker.

您可以通过两种方式解决它:
1)您worker在处理完所有排队的文件后没有完成。它等待AutoResetEvent下一个文件处理。在这种情况下,Executeworker通过调用通知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尚未启动,则将再次启动并处理未决的。workerworkerBackgroundWorker.RunWorkerCompletedfile

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

注意:如果您决定BackgroundWorker.RunWorkerCompleted在非 GUI 应用程序中使用,那么您应该小心,Start因为BackgroundWorker.RunWorkerCompleted不能在您调用的线程上调用Execute,并且会在Start. 更多信息:BackgroundWorker.RunWorkerCompleted 和线程

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

于 2014-02-21T09:29:00.930 回答
1

为了不用担心这个问题,当队列为空并且Start在worker退出之前被调用时,您可以尝试根本不离开worker方法:

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

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

没有不优雅的睡眠的另一种可能性是使用ManualResetEvent该类在队列为空并停止为空时发出信号。

于 2014-02-21T08:19:05.637 回答