我有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();
}
}
答案 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
类来表示队列为空时停止为空。