правильный способ использовать файл (ы) блокировки в качестве блокировки между несколькими процессами - PullRequest
5 голосов
/ 03 июня 2011

У меня есть ситуация, когда 2 разных процесса (мой C ++, другой, выполняемый другими людьми в JAVA) являются писателем и читателем из некоторого общего файла данных. Поэтому я пытался избежать состояния гонки, написав такой класс (РЕДАКТИРОВАТЬ: этот код не работает, это был просто пример)

class ReadStatus
{
    bool canRead;
public:
    ReadStatus()
    {
        if (filesystem::exists(noReadFileName))
        {
            canRead = false;
            return;
        }
        ofstream noWriteFile;
        noWriteFile.open (noWriteFileName.c_str());
        if ( ! noWriteFile.is_open())
        {
            canRead = false;
            return;
        }
        boost::this_thread::sleep(boost::posix_time::seconds(1));
        if (filesystem::exists(noReadFileName))
        {
            filesystem::remove(noWriteFileName);
            canRead= false;
            return;
        }
        canRead= true;
    }
    ~ReadStatus()
    {
        if (filesystem::exists(noWriteFileName))
            filesystem::remove(noWriteFileName);
    }
    inline bool OKToRead()
    {
        return canRead;
    }
};

использование:

ReadStatus readStatus; //RAII FTW
    if ( ! readStatus.OKToRead())
        return;

Это для одной программы ofc, у другой будет аналогичный класс. Идея заключается в следующем: 1. проверьте, не создала ли другая программа его «Я владелец файла», если он имеет перерыв, перейдите к 2. 2. создайте мой файл «Я владелец», проверьте еще раз, создал ли другая программа свою собственную программу, удалил ли он мой файл и остановился ли он, перейдите к 3. 3. сделайте мое чтение, затем удалите мое "Я владелец файла".

Обратите внимание, что редкие случаи, когда они оба не читают и не пишут в порядке, но проблема в том, что я все еще вижу небольшую вероятность возникновения условий гонки, потому что теоретически другая программа может проверить наличие моего файла блокировки, но один, затем я создаю свой, другая программа создает его, но прежде чем FS создаст его файл, я проверяю снова, и его там нет, тогда происходит катастрофа. Вот почему я добавил задержку в одну секунду, но, как ботаник CS, я нахожу неприятным, что такой код работает. Конечно, я не ожидаю, что кто-нибудь здесь напишет мне решение, но я был бы рад, если бы кто-то знал ссылку на надежный код, который я могу использовать. Постскриптум Это должны быть файлы, потому что я не пишу весь проект, и именно так это и должно быть сделано.

P.P.S .: доступ к файлу данных не читатель, писатель, читатель, писатель .... это может быть читатель, читатель, писатель, писатель, писатель, читатель, писатель ....

P.P.S: другой процесс не написан на C ++ :(, так что повышение не может быть и речи.

Ответы [ 3 ]

7 голосов
/ 03 июня 2011

В Unices традиционный способ сделать чистую блокировку на основе файловой системы - это использовать выделенные файлы блокировки с mkdir() и rmdir(), которые можно создавать и удалять атомарно с помощью одиночных системных вызовов.Вы избегаете гонок, никогда явно не проверяя существование блокировки - вместо этого вы всегда пытаетесь взять блокировку.Итак:

lock:
    while mkdir(lockfile) fails
        sleep

unlock:
    rmdir(lockfile)

Я полагаю, что это работает даже по NFS (что обычно плохо для такого рода вещей).

Однако вы, вероятно, также захотите взглянуть на правильную блокировку файлов, которая загружается лучше;Я использую F_SETLK / F_UNLCK fcntl блокировки для этого в Linux (обратите внимание, что они отличаются от блокировок стада, несмотря на название структуры).Это позволяет вам правильно блокировать, пока блокировка не будет снята.Эти блокировки также автоматически снимаются, если приложение умирает, что, как правило, хорошо.Кроме того, они позволят вам заблокировать ваш общий файл напрямую, без необходимости иметь отдельный файл блокировки.Это также работает в NFS.

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

3 голосов
/ 03 июня 2011

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

Если вы можете, взгляните на Boost.Interprocess , в части механизмов синхронизации.

1 голос
/ 03 июня 2011

Хотя я, как правило, против выполнения вызовов API, которые могут генерировать вызовы из конструктора / деструктора (см. Документацию по boost::filesystem::remove), или вообще создания вызовов без блока catch, это не совсем то, о чем вы спрашивали.

Вы можете проверить библиотеку Overlapped IO , если это для окон. В противном случае вы рассматривали возможность использования разделяемой памяти между процессами вместо этого?

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

...