2013-10-30 26 views
1

我正在使用System.IO.FileSystemWatcher在目录内的文件重命名上得到通知。这些文件是由不同进程创建的日志文件。与文件系统一起使用时的竞争状态

事件处理程序是这样的:

 private async void FileRenamedHandler(object sender, RenamedEventArgs e) 
     { 
      //when file is renamed 
      //try to upload it to a storage 
      //if upload is succesful delete it from disk 
     } 

一切看起来好到现在为止,但我需要补充的是,当该应用程序启动,以现有的日志文件上传到存储

通过目录遍历第二个方法

所以

public async Task UploadAllFilesInDirectory() 
    { 
     foreach (var file in Directory.GetFiles(_directoryPath)) 
     { 
      await TryUploadLogAsync(file); 
     } 
    } 

问题是我进入比赛状态一样,例如:

  • 文件刚刚被重命名并且FileRenamedHandler被触发,但同样的填充也会被UploadAllFilesInDirectory方法解析。在这一刻,我可能会上传两次相同的文件,或者当我试图从磁盘上删除它时,我会得到一个异常,因为它已被删除。

    我可以看到更多的竞争条件与此代码的情况。

任何想法如何我可以解决这个问题?

感谢

+1

如果这是所有相同的过程,为什么你不能同步这两种方法? – devshorts

+0

您可以指定哪些条件触发FileSystemWatcher,并删除重命名文件时激活的触发器。 – JConstantine

回答

2

可以使用ConcurrentDictionary跟踪当前正在处理的项目,并让它担心线程安全。

创建字典中的关键是文件路径(或其他识别对象),值是......任何。我们将此视为一套,而不是字典,但没有ConcurrentSet,所以这将不得不做。

然后对于每个文件,您必须处理呼叫TryAdd。如果它返回true,则添加该对象,并且可以处理该文件。如果它返回false,那么该文件就在那里,并在其他地方处理。

然后,您可以删除对象,你就大功告成了处理它的时候:

//store this somewhere 
var dic = new ConcurrentDictionary<string, string>(); 

//to process each file 
if (dic.TryAdd(path, path)) 
{ 
    //process the file at "path" 
    dic.TryRemove(path, out path); 
} 
+0

我喜欢你的方法+1谢谢 –

1

我建议建立一个队列和存储文件上传为某种工作到队列中。如果您处理队列中的项目,您可以在尝试上载之前检查每个文件的存在情况。