В настоящее время у нас есть одно приложение, которое отслеживает папку для новых файлов. Чтобы сделать его отказоустойчивым и иметь возможность обрабатывать больше файлов одновременно, мы хотим иметь возможность запускать несколько экземпляров этого приложения на разных компьютерах. Мы используем File.Move
для "блокировки" файла и убедитесь, что только один поток может обрабатывать файл за раз.
Чтобы проверить, что только одно приложение и/или поток могут выполнить File.Move
в файле, я создал простое приложение (на основе исходного кода приложения), которое создало 10 потоков на приложение и контролировало папку, когда каждый thread обнаруживает новый файл, он выполняет File.Move
на нем и изменяет расширение файла, чтобы попытаться остановить другой поток от того же самого.
Я видел проблему при запуске нескольких копий этого приложения (и он запускался сам по себе), в результате чего два потока (либо в одном приложении, либо разные) успешно выполняли File.Move
без исключения, но поток, который выполнял его последний (я изменяю расширение файла, чтобы включить DateTime.Now.ToFileTime()
), успешно переименовал файл.
Я посмотрел, что делает File.Move
, и он проверяет, существует ли файл до его выполнения, затем он вызывает Win32Native.MoveFile
для выполнения перемещения.
Все остальные потоки/приложения вызывают исключение, как и я.
Причины, по которым это проблема:
- Я думал, что только один поток может выполнять
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
- это просто последовательный номер, назначенный каждому потоку для ведения журнала.
Я запускаю Windows 7 Enterprise SP1 на SSD с NTFS.