2016-07-01 4 views
1

У меня есть сценарий, в котором мы поддерживаем файл тарифов (.xml), который является доступом трех разных приложений, работающих на 3 разных серверах. Все 3 приложения используют RateMaintenance.dll, который имеет менее 4 методов Load, Write, Read и Close.C# Чтение/запись файла из нескольких приложений

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

По моему пониманию, это означает, что по какой-то причине приложение 3 пытается получить доступ к тому же самому. Не могли бы вы предложить, как справиться с таким сценарием?

 Monitor.Enter(RatesFileLock); 

     try 
     { 
      //Open Rates file 
      LoadRatesFile(false); 

      //Write Rates into file 
      WriteRatesToFile(); 

      //Close Rates file 
      CloseRatesFile(); 
     } 
     finally 
     { 
      Monitor.Exit(RatesFileLock); 
     } 

Метод подписи load-

LoadRatesFile(bool isReadOnly) 

Для открытия файл-

new FileStream(RatesFilePath, 
     isReadOnly ? FileMode.Open : FileMode.OpenOrCreate, 
     isReadOnly ? FileAccess.Read : FileAccess.ReadWrite, 
       isReadOnly ? FileShare.ReadWrite : FileShare.None); 

.... remaining Rates reading logic code here 

для чтения Цены от

file-
Rates = LoadRatesFile(true); 

для написания ставок во Файл-

if (_RatesFileStream != null && _RatesInfo != null && _RatesFileSerializer != null) 
      { 
        _RatesFileStream.SetLength(0); 

        _RatesFileSerializer.Serialize(_RatesFileStream, _RatesInfo); 
      } 

В заключительном файле

ме-
  _RatesFileStream.Close(); 
      _RatesFileStream = null; 

Я надеюсь, я пытаюсь объяснить мой сценарий в деталях. Пожалуйста, дайте мне знать на всякий случай.

+0

Что в вашем трассировки стека? Вероятно, вы говорите, что «файл используется другим процессом»? – tier1

+0

Также имейте в виду, что каждый запущенный процесс будет иметь свой собственный экземпляр «RatesFileLock». – tier1

+0

да ... я понимаю это. Эта ошибка возникает один раз в месяц, поэтому не удается собрать трассировку стека. но, безусловно, это проблема с открытием файла, поскольку он используется другим приложением. как я могу это преодолеть? – Sagar

ответ

2

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

Прежде чем я покажу один способ сделать это, у меня есть два незначительных предложения. Блоки «использования» C# действительно полезны для работы с такими ресурсами, как файлы и блокировки, которые вы действительно хотите утилизировать после использования ,В вашем коде монитор всегда выходит из-за того, что вы используете try..finally (хотя это все равно было бы более ясным с внешним блоком блокировки), но вы не закрываете файл, если метод WriteRatesToFile терпит неудачу.

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

private static object _ratesFileLock = new object(); 

public void UpdateRates() 
{ 
    lock (_ratesFileLock) 
    { 
     using (var stream = GetRatesFileStream()) 
     { 
      var rates = LoadRatesFile(stream); 

      // Apply any other update logic here 

      WriteRatesToFile(rates, stream); 
     } 
    } 
} 

private Stream GetRatesFileStream() 
{ 
    return File.Open("rates.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); 
} 

private IEnumerable<Rate> LoadRatesFile(Stream stream) 
{ 
    // Apply any other logic here 
    return RatesSerialiser.Deserialise(stream); 
} 

private void WriteRatesToFile(IEnumerable<Rate> rates, Stream stream) 
{ 
    RatesSerialiser.Serialise(rates, stream); 
} 

Это пытается открывает файл потока один раз, а затем повторно его между нагрузкой и записать - и надежно распоряжаться он, даже если ошибка встречается внутри блока использования (то же самое относится к блоку блокировки, который проще, чем Monitor.Enter/Exit и try..finally).

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

private static object _ratesFileLock = new object(); 

public void UpdateRates() 
{ 
    Attempt(TryToUpdateRates, maximumNumberOfAttempts: 50, timeToWaitBetweenRetriesInMs: 100); 
} 

private void TryToUpdateRates() 
{ 
    lock (_ratesFileLock) 
    { 
     using (var stream = GetRatesFileStream()) 
     { 
      var rates = LoadRatesFile(stream); 

      // Apply any other update logic here 

      WriteRatesToFile(rates, stream); 
     } 
    } 
} 

private Stream GetRatesFileStream() 
{ 
    return File.Open("rates.txt", FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite); 
} 

private IEnumerable<Rate> LoadRatesFile(Stream stream) 
{ 
    // Apply any other logic here 
    return RatesSerialiser.Deserialise(stream); 
} 

private void WriteRatesToFile(IEnumerable<Rate> rates, Stream stream) 
{ 
    RatesSerialiser.Serialise(rates, stream); 
} 

private static void Attempt(Action work, int maximumNumberOfAttempts, int timeToWaitBetweenRetriesInMs) 
{ 
    var numberOfFailedAttempts = 0; 
    while (true) 
    { 
     try 
     { 
      work(); 
      return; 
     } 
     catch 
     { 
      numberOfFailedAttempts++; 
      if (numberOfFailedAttempts >= maximumNumberOfAttempts) 
       throw; 
      Thread.Sleep(timeToWaitBetweenRetriesInMs); 
     } 
    } 
} 
+0

Спасибо @ Dan за ваши предложения. Я обязательно попробую это. – Sagar

+0

@Sagar - можете ли вы использовать это, чтобы облегчить вашу проблему? Есть ли что-нибудь, что вы хотели бы добавить или уточнить, прежде чем принимать ответ? –

+0

Я изменил существующий код согласно вашему предложению в понедельник, 4 июля. До сих пор я не получил эту проблему в тестовой среде. Поскольку этот вопрос встречается очень редко, пожалуйста, дайте мне еще немного времени, прежде чем я помету в качестве ответа. – Sagar

3

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

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

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

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

+0

Спасибо за ваш ответ, но из-за ограничений бюджета проекта я не могу ввести базу данных. – Sagar