使用文件系统时的竞争条件

时间:2013-10-30 17:13:57

标签: c# .net race-condition

我正在使用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方法解析。在这一刻,我可能会上传相同的文件两次,或者在尝试从磁盘中删除它时会出现异常,因为它已被删除。

    我可以通过此代码查看更多竞争条件案例。

知道如何解决这个问题吗?

谢谢

2 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:1)

我建议构建一个队列,并将要上传的文件存储为队列中的某种作业。如果您处理队列中的项目,则可以在尝试上传之前检查每个文件是否存在。