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

2 つの方法で解決できます:
1)workerキューに入れられたすべてのファイルを処理した後、終了しません。AutoResetEvent次のファイルが処理されるのを待ちます。その場合、を呼び出して にExecute通知する必要があります。 例:workerAutoResetEvent.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();

: GUI 以外のアプリケーションで使用することにした場合は、呼び出し先のスレッドではなく を呼び出すことができ、 で競合状態が発生するためBackgroundWorker.RunWorkerCompleted、注意が必要です。詳細: BackgroundWorker.RunWorkerCompleted とスレッド化StartBackgroundWorker.RunWorkerCompletedExecuteStart

Start()2 つの異なるスレッドから同時に呼び出すと、両方がそれを確認できworker.IsBusy == false、両方が を呼び出しますworker.RunWorkerAsync()worker.RunWorkerAsync()別のスレッドより少し遅れて呼び出すスレッドは、 InvalidOperationException. そのため、その例外をキャッチするか、IsBusy+RunWorkerAsyncをロックでクリティカル セクションにラップして、競合状態と例外のスローを回避する必要があります。

于 2014-02-21T09:29:00.930 に答える
1

問題を心配する必要がないようにするには、キューが空でStartワーカーが終了する前に呼び出された場合、ワーカー メソッドをまったく離れないようにすることができます。

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

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

他の可能性として、非エレガントな睡眠なしで、ManualResetEventクラスを使用して、キューが空になり、空でなくなったときに通知することです。

于 2014-02-21T08:19:05.637 に答える