2013-11-15 4 views
3

Сценарий, в котором у меня есть 10 000 XML-файлов, которые я хочу прочитать и сохранить в базе данных. У меня есть 5 служб Windows, которые все попадают в папку, чтобы попытаться обработать.C# File Move (Rename) Multi Thread Windows OS

Моя техника - это сначала попробовать и переименовать (File.Move) файл с расширением, специфичным для данного Сервисного экземпляра.

Это работает в 99% случаев. Однако то, что я вижу, это файловая система. 0,01% времени позволяют двум запросам попробовать и переименовать ТОЧНО в то же время.

Как я могу предотвратить это? Имеет ли это смысл? См. Следующий фрагмент кода, чтобы получить представление. В итоге у меня около 10 файлов, которые являются исключениями ввода-вывода.

string[] sourceFiles = Directory.GetFiles(InputPath, string.Format(LocaleHelper.Culture, "*.{0}", Extention)) 
              .OrderBy(d => new FileInfo(d).CreationTime).ToArray(); 


      foreach (string file in sourceFiles) 
      { 
       var newFileName = string.Format(LocaleHelper.Culture, "{0}.{1}", file, CacheFlushManager.GetInstanceName); 


       try 
       { 
        // first we'll rename // however at this point the file may not even exist 
        // it will throw an exception and move onto the next file if it exists 


        File.Move(file, newFileName); 



        var xml = File.ReadAllText(newFileName); 

        // write to DB at this point we know its unique 
       } 
       catch (FileNotFoundException ex) 
       { 
        Logger.LogDebug(string.Format(LocaleHelper.Culture, "{0} Couldn't read file : {1}", CacheFlushManager.GetInstanceName, newFileName)); 
       } 
       catch (IOException ex) 
       { 
        Logger.LogDebug(string.Format(LocaleHelper.Culture, "{0} Couldn't process file : {1}", CacheFlushManager.GetInstanceName, newFileName)); 
       } 
       catch (Exception ex) 
       { 
        Logger.LogError("Execute: Error", ex); 

        try 
        { 
         File.Move(newFileName, string.Format(LocaleHelper.Culture, "{0}.badfile", newFileName)); 
        } 
        catch (Exception ex_deep) 
        { 
         Logger.LogError(string.Format("{0} Execute: Error Deep could not move bad file {1}", CacheFlushManager.GetInstanceName, newFileName), ex_deep); 
        } 
       } 

EDIT 1

Ниже точная ошибка в качестве примера того, что я вижу. Я очень смущен тем, как файл действует, что точное время может быть использовано на основе кода, который я использую? Неужели я полностью из этого сорняка?

[7220] TransactionFileServiceProcess [11:28:32]: Service4 не удалось обработать файл: C: \ Temp \ Input \ yap804.xml.Service4 System.IO.IOException: Процесс не может получить доступ к файлу ' C: \ temp \ Input \ yap804.xml.Service4 ', потому что он используется другим процессом.

EDIT 2

Вот посмотрите на то, что происходит с «отладки» точки зрения. Как оба сервиса 2 & 3 попадают в «END RENAME?»? Я думаю, что это суть проблемы ... мысли?

Проблема в файле yap620.xml.Service3 в конечном итоге будет просто сидеть там из-за ошибки операции с файлом.

[6708] TransactionFileServiceProcess [10:54:38]: Service3 Start Rename: C:\temp\Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug =>  LoggerImpl.Write E[] 

[4956] TransactionFileServiceProcess [10:54:38]: Service2 Start Rename: C:\temp\Input\yap620.xml.Service2 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 

[7416] TransactionFileServiceProcess [10:54:38]: Service4 Start Rename: C:\temp\Input\yap620.xml.Service4 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 

[6708] TransactionFileServiceProcess [10:54:38]: Service3 End Rename: C:\temp\Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 

[6708] TransactionFileServiceProcess [10:54:38]: Service3 Start Read: C:\temp\Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 

[4956] TransactionFileServiceProcess [10:54:38]: Service2 End Rename: C:\temp\Input\yap620.xml.Service2 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 

[4956] TransactionFileServiceProcess [10:54:38]: Service2 Start Read: C:\temp\Input\yap620.xml.Service2 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 

[6708] TransactionFileServiceProcess [10:54:38]: Service3 Couldn't process file : C:\temp \Input\yap620.xml.Service3 TransactionFileServiceProcess.Execute => BHSLogger.LogDebug => LoggerImpl.Write E[] 
+0

10 000 XML-файлов? почему бы не использовать JSON? – thenewseattle

+0

У меня нет выбора, к какому файловому формату они относятся, к сожалению. И в этом случае это не имело бы значения. – aherrick

+0

Попытка устранить столкновение не имеет смысла - у вас, похоже, уже достаточно кода, чтобы избежать этого - так что просто ожидайте, что столкновения (или любые другие ошибки ввода-вывода) произойдут и повторите попытку. –

ответ

0

У вас есть несколько потоков, работающих в одной службе? Или несколько независимых сервисов?

Если у вас несколько потоков в одной службе, просто создайте Queue<FileInfo> или что-то подобное и удалите элементы из очереди, когда потоки могут обрабатываться бесплатно. Я считаю, что стандарт Queue является потокобезопасным, поэтому вы никогда не должны обрабатывать один и тот же файл дважды.

Если у вас есть несколько независимых услуг, вы можете посмотреть на использование LockFile или File.Open с указанным FileShare.None.

редактировать:

Я понял, что вы пытаетесь сделать. Я думал, вы хотите, чтобы все файлы обрабатывались каждой из служб. Вам действительно нужно запустить несколько потоков в одной службе или разрешить некоторый способ связи, который позволяет различным службам определять, какой из файлов уже обработан.

+0

Эй, чувак .. так много независимых сервисов, каждый с 1 потоком. Как использовать LockFile для переименования? – aherrick

+0

Вы не переименовали бы, вы просто приобрели бы блокировку, и любая служба, пытавшаяся получить доступ к файлу в одно и то же время, либо дождалась бы, чтобы получить блокировку, либо запомнила этот файл, либо повторит попытку позже, т). – FlyingStreudel

+0

, но я хочу, если есть 10 файлов и 5 процессов обслуживания, каждый процесс по существу обрабатывается. 2. – aherrick

1

Я не вижу, где проблема. У вас есть несколько потоков, которые получают список файлов, а затем пытаются обрабатывать эти файлы. Иногда файл, который поток пытается переименовать, не существует, и иногда файл существует, но он находится в процессе переименования в другой поток.Ни одна из этих двух проблем не должна быть проблемой. В любом случае поток, который получает ошибку, должен просто предположить, что какой-то другой поток обрабатывает файл и перемещается дальше.

Предполагая, конечно, что у вас нет другого процесса, который обращается к файлам в этом каталоге.

Почему вы хотите, чтобы пять отдельных экземпляров службы делали это, это вне меня. Вы можете немного упростить ситуацию и сократить ненужные операции ввода-вывода, выполнив только один процесс: Parallel.ForEach. Например:

string[] sourceFiles = Directory.GetFiles(
    InputPath, 
    string.Format(LocaleHelper.Culture, "*.{0}", Extention)) 
    .OrderBy(d => new FileInfo(d).CreationTime).ToArray(); 

Parallel.Foreach(sourceFiles, (file) => 
{ 
    // do file processing here 
}); 

TPL выделит несколько потоков для выполнения обработки, а также назначить рабочие элементы на резьбе. Поэтому нет никаких шансов, что файл будет открыт несколькими потоками.

+1

Эй, спасибо за ответ. Чтобы ответить на ваш вопрос для сценария балансировки нагрузки. Предположим, у них было 5 разных серверов в домене. На каждом сервере была запущена служба, указывающая на общий ресурс папки. – aherrick