[英]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.