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