День добрый,
прошу помощи, ибо что-то я подвисла там, где совершенно не ожидала...
Ситуация:
есть приложение (пока как прототип, дабы понять насколько оно нам будет полезно), которое должно помогать коллегам в поддержании актуальной информации о всех наших компонентах - с одной стороны, это инфа о самих компонентах как название, страна и город, тип и т.д., с другой - кто кому родственник, т.е. какие компоненты и кому нужны, чтобы можно было сдать очередную версию компоненты (например, линукс является составляющей компоненты К1, которую производим мы сами).
Т.к. задача была поставлена предельно четко - прототип с минимальными затратами времени, то в виде базы данных я использовала обычные файлы (тхт или csv - в зависимости от данных).
Пока работает 1 человек, все супер. Но! Естественно это утопия
Работать должны в идеале сразу несколько коллег параллельно.
И тут всплывает вопрос - как защитить доступ к файлам в момент чтения - записи.
Идея была проста
:
- есть класс Helper, где лежат различные общие для всех функции, в частности функции чтения - записи файлов.
Я решила обезопасить себя с двух сторон, посему ввела 2 мутекса - один стоит на самих функциях чтения - записи файлов и обявлен как private static, второй - общий для всех операций, включается каждый раз, когда кто-то нажимает на кнопку "сохранить" (т.о. я безопасно выполняю группу функций):
public class Helpers
{
private static Mutex m_MutexForReadWriteFile;
public static Mutex MutexForOperations;
public Helpers()
{
m_MutexForReadWriteFile = CreateMutex(@"m_MutexForReadWriteFile");
MutexForOperations = CreateMutex(@"CommonMutexForOperations_ReadWriteFile");
}
private Mutex CreateMutex(string mutexName)
{
Mutex mutexToCreate = null;
bool mutexExists = true;
try
{
mutexToCreate = Mutex.OpenExisting(mutexName);
}
catch (WaitHandleCannotBeOpenedException ex)
{
mutexExists = false;
}
if (!mutexExists)
mutexToCreate = new Mutex(false, mutexName);
return mutexToCreate;
}
*************** Чтение - запись файлов - начало**************************************
private FileStream CreateFileStreamForReadWriteFile(string wholeFileName, FileMode mode, FileAccess access)
{
return new FileStream(wholeFileName, mode, access, FileShare.ReadWrite);
}
public Dictionary<int, string> ReadTextFile(string fileName, out Int64 updateCounter, string path = "")
{
Dictionary<int, string> fileContent = null;
m_MutexForReadWriteFile.WaitOne();
{
using (FileStream fileStreamTxt = CreateFileStreamForReadWriteFile(wholeFileName, FileMode.Open, FileAccess.Read))
{
using (System.IO.StreamReader txtFileToRead =
new System.IO.StreamReader(fileStreamTxt))
{
while ((oneLine = txtFileToRead.ReadLine()) != null)
{
// тут читаем / сохраняем...
}
}
txtFileToRead.Close();
}
}
}
m_MutexForReadWriteFile.ReleaseMutex();
return fileContent;
}
public void WriteInfosInFile(string fileName, StringBuilder allInfosToSaveInFile, bool appendNewItemInExistingFile = false, string path = "" )
{
m_MutexForReadWriteFile.WaitOne();
{
using (FileStream fileStream = CreateFileStreamForReadWriteFile(wholeFileName, FileMode.Create, FileAccess.Write))
{
using (StreamWriter fileToWrite = new StreamWriter(fileStream))
{
fileToWrite.Write(allInfosToSaveInFile.ToString());
fileToWrite.Close();
}
}
}
m_MutexForReadWriteFile.ReleaseMutex();
}
*************** Чтение - запись файлов - конец**************************************
*************** выполнение группы функций при нажатии на кнопку "сохранить" - начало**************************************
public static bool MutexStartWaiting()
{
bool isMutexFree = false;
try
{
isMutexFree = MutexForOperations.WaitOne(1000);
}
catch(AbandonedMutexException ex)
{
MutexRelease();
isMutexFree = MutexForOperations.WaitOne(1000);
}
return isMutexFree;
}
public static void MutexRelease()
{
MutexForOperations.ReleaseMutex();
}
// основная функция - ее вызывают все при нажатии на кнопку "сохранить"
public void SaveChangesSafetyBetweenApplications(ISaveChangesWithMutexSupport currentObjectToNeedSaving,
ArrayList infoToSave)
{
MutexStartWaiting();
currentObjectToNeedSaving.SaveChangesSafety(infoToSave);
MutexRelease();
}
*************** выполнение группы функций при нажатии на кнопку "сохранить" - конец**************************************
}
Любой класс, вызывающий процесс сохранения информации выглядит где-то так:
class AddOrChangeColor : AddOrChangeDataBase, ISaveChangesWithMutexSupport
{
protected Helpers m_Helper; // обявлена и создана в базовом классе
public override void SaveData()
{
string oldColor = m_DataContainerWithAllInfosAboutComponents.AllExistingComponentColors[m_IndexOfModifiedColorInList];
// тут я вызываю основную функцию, использующую глобальный мутекс
m_Helper.SaveChangesSafetyBetweenApplications(this, new ArrayList() { oldColor });
}
// функция, вызываемая после установления глобального мутекса (принадлежит интерфейсу ISaveChangesWithMutexSupport)
// в связи с тем, что данные хранятся в файлах и возможны обновления, сначала я считываю текущее состояние файла с данными и перезаписываю контейнер, используя свежие данные (при этом я надеюсь, что никто другой в этот момент ничего в этот файл больше записать не может).
Потом проверяю, не сделал ли кто-то другой уже такие же изменения. Если нет - спокойно вношу свои изменения и перезаписываю файл. После чего освобождаю мутекс
public void SaveChangesSafety(ArrayList infoToSave)
{
m_Helper.RefreshDataFromConfigFileIfNecessary(m_DataContainerWithAllInfosAboutComponents,
DataContainer.UpdateCountersIndexies.AllExistingComponentColors,
Constants.c_Filename_Config_Colors, "",
m_DataContainerWithAllInfosAboutComponents.ReadColorsFromFile);
if (m_SaveDataIsCurrentOperation)
SaveChangesSafety_SaveDataIsCurrentOperation(infoToSave);
else
SaveChangesSafety_DeleteDataIsCurrentOperation(infoToSave);
}
}
Вроде бы - ничего не предвещало... Однако - не работает... Для теста я стартую свое приложение на 2 компах (благо имею 2 штуки на рабочем столе) с разницей в минуту, дабы мутексы в одном приложении были созданы , после чего открываю одну и ту же форму, вношу 2 разных строки для обозначения цвета (просто я тут привела пример этого класса ) и одновременно нажимаю на "сохранить". Результат - сохраняет только одно из значений...
Начала с дебагом проверять создание мутексов и нашла проблему - каждая инстанция приложения создает свои мутексы! хотя после старта первой инстанции они уже существуют!
Вопрос - и где я идиот?...