【发布时间】:2014-09-19 10:40:36
【问题描述】:
我们目前有一个应用程序可以监控文件夹中的新文件。为了使其具有容错性并能够一次处理更多文件,我们希望能够在不同的机器上运行该应用程序的多个实例。我们使用File.Move 来“锁定”一个文件,并确保一次只有一个线程可以处理一个文件。
为了测试只有一个应用程序和/或线程可以对文件执行File.Move,我创建了一个简单的应用程序(基于原始应用程序的代码),它为每个应用程序创建 10 个线程并监视一个文件夹,当每个线程检测到一个新文件,它对其执行File.Move 并更改文件的扩展名,以尝试阻止其他线程执行相同操作。
我在运行此应用程序的多个副本(并且它自己运行)时看到了一个问题,其中 2 个线程(在同一个应用程序中或不同的应用程序中)都成功执行 File.Move 没有抛出异常,但是最后执行它的线程(我将文件的扩展名更改为包含DateTime.Now.ToFileTime()),成功地重命名了文件。
我查看了File.Move 的作用,它在执行操作之前检查文件是否存在,然后调用Win32Native.MoveFile 执行移动。
正如我所料,所有其他线程/应用程序都会抛出异常。
这是一个问题的原因是:
- 我认为一次只有 1 个线程可以对文件执行
File.Move。 - 我需要可靠地让一个应用程序/线程一次能够处理一个文件。
这是执行File.Move的代码:
public bool TryLock(string originalFile, out string changedFileName)
{
FileInfo fileInfo = new FileInfo(originalFile);
changedFileName = Path.ChangeExtension(originalFile, ".original." + DateTime.Now.ToFileTime());
try
{
File.Move(originalFile, changedFileName);
}
catch (IOException ex)
{
Console.WriteLine("{3} - Thread {1}-{2} File {0} is already in use", fileInfo.Name, Thread.CurrentThread.ManagedThreadId, id, DateTime.Now.ToLongTimeString());
return false;
}
catch (Exception ex)
{
Console.WriteLine("{3} - Thread {1}-{2} File {0} error {4}", fileInfo.Name, Thread.CurrentThread.ManagedThreadId, id, DateTime.Now.ToLongTimeString(), ex);
return false;
}
return true;
}
注意 - id 只是我分配给每个线程以进行日志记录的序号。
我正在使用 NTFS 的 SSD 上运行 Windows 7 Enterprise SP1。
【问题讨论】:
-
你为什么不用
lock? -
move = 复制到新的目的地,然后删除原来的。
-
@YuvalItzchakov 这应该只允许一个线程打开文件,但正如下面的答案所述,我们可能会在释放锁和删除文件时进入竞争状态。
-
你有分布式架构吗?
-
@K.B 是的,我们有 4 个内部生产服务器,但目前只有一个处于活动状态(由于可靠的文件“锁定”)。我们架构的其余部分由 NServiceBus、MSMQ、Azure Worker Roles 和 Azure Service Bus 组成。
标签: c# multithreading file-io