Как управлять одновременным вводом/выводом доступа к файлу XML из нескольких экземпляров EXE с помощью Delphi.

У меня есть инструмент командной строки, написанный на Delphi, задача которого состоит в том, чтобы вставить узел в XML-файл, а затем немедленно выйти. Мне нужно сделать так, чтобы несколько экземпляров инструмента выполнялись одновременно и вставляли узлы в один и тот же XML.

Для достижения этой цели я представил простой файл «mutex» — инструмент создает один временный файл перед записью в XML, а затем удаляет временный файл после завершения записи. Поэтому, если выполняется другой экземпляр, он проверяет наличие этого временного файла и ждет, пока он не будет удален. Затем он снова создает временный файл, записывает в XML и удаляет временный файл.

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

Есть ли лучший способ заставить его работать с большим количеством экземпляров, работающих и одновременно записывающих в XML?


person m_pGladiator    schedule 19.09.2008    source источник
comment
Спасибо всем за ответы. Сначала у меня была логическая ошибка в моем коде, и я ее исправил, но метод, который я использую, по-прежнему не годится. На следующей неделе я продолжу работу над этим небольшим инструментом, а затем напишу здесь обновление и приму наиболее подходящий ответ.   -  person m_pGladiator    schedule 22.09.2008
comment
Похоже, клиент-серверное решение здесь подойдет...   -  person Bob A    schedule 18.12.2011


Ответы (5)


Именованный семафор или мьютекс может сделать это за вас на одной машине. Используйте, например. TMutex из SyncObjs и используйте один из конструкторов, который принимает аргумент имени. Если вы используете одно и то же имя во всех приложениях, они будут синхронизироваться через один и тот же мьютекс ядра. Используйте TMutex.Acquire для доступа и TMutex.Release, когда закончите, защищенный в блоке try/finally.

Используйте перегрузку TMutex.Create с аргументом InitialOwner, но укажите для этого значение False (если, конечно, вы не хотите сразу получить мьютекс). Эта перегрузка вызывает CreateMutex за кулисами. Посмотрите исходный код SyncObjs и документацию для CreateMutex для получения дополнительной информации.

person Barry Kelly    schedule 19.09.2008
comment
Наконец, я снова зациклился на этом проекте, и мьютекс решил проблему. 10x :) - person m_pGladiator; 17.10.2008

1 - Настройте файл, который записывает ожидающие изменения (он будет работать как очередь)

2. Напишите простое приложение для просмотра этого файла и примените изменения к XML-файлу.

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

Теперь только одно приложение должно работать с окончательным XML-файлом.

person JosephStyons    schedule 19.09.2008
comment
Наверное, проще создать директорию, в которую нужные изменения добавляются отдельными файлами. Тогда нет файла для добавления, просто бросьте и запустите. - person mj2008; 19.09.2008
comment
Но все экземпляры должны снова записывать в тот же файл ожидающих изменений. Это только перенаправит проблему в другой файл. Идея с каталогом действительно сработает. - person m_pGladiator; 16.10.2008

TXMLDocument уже предотвращает одновременную запись нескольких экземпляров в один и тот же файл. Итак, я предполагаю, что на самом деле ваш вопрос означает следующее: "Как я могу открыть XML-документ для чтения, запретить другим экземплярам запись в документ, пока я его читаю, а затем записать в документ, прежде чем позволить другим экземплярам сделать то же самое?»

В этом случае вы должны сами открывать и закрывать файл, а не позволять TXMLDocument делать это за вас. Используйте TFileStream, чтобы открыть файл с эксклюзивной блокировкой чтения и записи и XMLDocument.LoadFromStream вместо LoadFromFile. Сохраните документ с помощью SaveToStream после сброса stream.Position в 0. Используйте try/finally, чтобы убедиться, что вы закрыли поток, когда закончите с ним. Поскольку вы блокируете исключительно файл, вам больше не нужен временный файл или любой другой мьютекс.

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

person Community    schedule 19.09.2008

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

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

person skamradt    schedule 19.09.2008

Из этого ответа:

В Windows это возможно, если вы можете управлять обеими программами. LockFileEx. Для чтения откройте общий замок в файле блокировки. Для записи откройте монопольную блокировку файла блокировки. Блокировка в Windows выглядит странно, поэтому я рекомендую использовать для этого отдельный файл блокировки.

(«Обе программы» не применяются в вашем случае, это одна и та же программа, просто работающая в нескольких экземплярах.)

Боковое примечание / как я нашел этот ответ: библиотека ведения журнала Java logback использует API блокировки файлов для конкретной платформы (через NIO) для реализации «разумного режима', в котором несколько процессов могут войти в тот же файл без его повреждения - то, что iiuc невозможно при операциях с файлами Delphi RTL.

person mjn    schedule 18.12.2011