1

我有一個FileSystemWatcher它正在尋找新的文件,把文件名在Queue。 在單獨的線程中,隊列已關閉。我的代碼正在工作,但由於異步過程,我懷疑是否會丟失信息。敬請收看由評論解釋代碼: (我想,也許我需要這樣的東西線程鎖的地方?) (代碼爲簡體)BackgroundWorker和ConcurrentQueue

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

} 
+0

請不要在標題中包含languge標籤,除非在沒有標籤的情況下才有意義。標籤用於此目的。 –

回答

2

是的,你可能有問題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,因爲它很忙,那麼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換成帶鎖的關鍵部分以避免競爭條件和異常拋出。

+0

謝謝!我用你的第二個解決方案。是的,它是一個非GUI應用程序。感謝您的注意! 「Execute」由FileSystemWatcher創建的事件調用,所以它已經由不同的線程執行,但它仍然是同步的? – JDeuker

+0

@ J.D。我在回答中添加了一條註釋以解決您的問題。我希望我能正確理解你的問題。 –

+0

幫了我很多,謝謝! – JDeuker

1

爲了不擔心這個問題,當隊列空Start是工人退出之前叫,你可以嘗試不會離開的輔助方法都:

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

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

其他可能性,沒有不雅的睡眠會使用ManualResetEvent上課的時候隊列爲空信號和停止空着。

+0

問題不是隊列本身。隊列工作正常,穩定。我很擔心在出隊完成後入隊的項目。請看看我在哪裏放置「//線程鎖?」評論 – JDeuker

+0

@ J.D。目前還不清楚你想達到什麼目標。這裏沒有代碼,所以顯然不需要鎖。至於添加項目 - 'Enqueue'方法也是同步的。 – BartoszKP

+0

是的,之後沒有太多的代碼,但我認爲可能還有幾毫秒的通過......我只是想盡可能地安全。 – JDeuker