Программа прерывает зависание названного мьютекса - PullRequest
0 голосов
/ 24 апреля 2019

У меня есть несколько процессов, но только один должен быть запущен одновременно. Это означает, что, скажем, Process1 запущен и, если Process2 запущен, то Process2 должен дождаться завершения Process1 . Я рассматриваю boost named_mutex для этой цели. Чтобы избежать сценария, в котором мьютекс может не освобождаться при возникновении какого-либо исключения, похоже, что boost :: lock_guard может быть полезным. Я придумал следующую упрощенную версию кода.

#include <iostream>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/thread.hpp>
#include <chrono>
#include <thread>

using namespace boost::interprocess;
#pragma warning(disable: 4996)
int main()
{


    std::cout << "Before taking lock" << std::endl;

    named_mutex mutex(open_or_create, "some_name");
    boost::lock_guard<named_mutex> guard(mutex) ;

    // Some work that is simulated by sleep
    std::cout << "now wait for 10 second" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(10));

    std::cout << "Hello World";


}

Пока все хорошо. Когда эта программа работает, я нажимаю Ctl + C, чтобы программа была прервана (симуляция сбоя программы, необработанное исключение и т. Д.). После этого, когда я запускаю приложение, программа зависает в следующей строке кода.

named_mutex mutex(open_or_create, "some_name");
boost::lock_guard<named_mutex> guard(mutex) ;

Если я изменю имя мьютекса, то оно будет работать нормально, не зависая. Однако, похоже, что мьютекс с именем some_name каким-то образом «запоминается» на машине в каком-то плохом состоянии. Это приводит к тому, что любое приложение, которое пытается получить мьютекс с именем some_name , зависает на этой строке кода. Если я изменю это имя мьютекса на « some_name2 », программа снова будет работать нормально.

  1. Может кто-нибудь объяснить, чем вызвано такое поведение?
  2. Как я могу сбросить поведение для этого конкретного мьютекса?
  3. Самое главное, как избежать этого сценария в реальном приложении?

Ответы [ 2 ]

2 голосов
/ 24 апреля 2019

Как объяснено в этом ответе на вопрос, связанный @ppetraki выше , boost::interprocess:named_mutex, к сожалению, использует блокировку файла в Windows, а не фактический мьютекс.Если ваше приложение завершается ненормально, эта блокировка файла не будет удалена из системы.На самом деле это к открытой проблеме .

Глядя на исходный код , мы видим, что, если определено BOOST_INTERPROCESS_USE_WINDOWS, internal_mutex_type отображается наwindows_named_mutex, который внутренне, использует windows_named_sync, который , кажется, просто использует блокировку файла в конце.Я не уверен, что именно является обоснованием этого выбора реализации.Как бы то ни было, похоже, нет никакого способа заставить boost::interprocess использовать правильный мьютекс в Windows.Я бы предложил просто создать именованный мьютекс, используя CreateMutex , например:

#include <type_traits>
#include <memory>
#include <stdexcept>
#include <mutex>
#include <iostream>

#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

struct CloseHandleDeleter { void operator ()(HANDLE h) const { CloseHandle(h); } };

class NamedMutex
{
    std::unique_ptr<std::remove_pointer_t<HANDLE>, CloseHandleDeleter> m;

public:
    NamedMutex(const wchar_t* name)
        : m(CreateMutexW(nullptr, FALSE, name))
    {
        if (!m)
            throw std::runtime_error("failed to create mutex");
    }

    void lock()
    {
        if (WaitForSingleObject(m.get(), INFINITE) == WAIT_FAILED)
            throw std::runtime_error("something bad happened");
    }

    void unlock()
    {
        ReleaseMutex(m.get());
    }
};

int main()
{
    try
    {
        NamedMutex mutex(L"blub");

        std::lock_guard lock(mutex);

        std::cout << "Hello, World!" << std::endl;
    }
    catch (...)
    {
        std::cerr << "something went wrong\n";
        return -1;
    }

    return 0;
}
1 голос
/ 24 апреля 2019

Может кто-нибудь объяснить, чем вызвано такое поведение?

Мьютекс является глобальным.

Как я могу сбросить поведение для этого конкретного мьютекса?

Звоните boost::interprocess::named_mutex::remove("mutex_name");

Самое главное, как избежать этого сценария в реальном приложении?

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

Обновление:

Я понимаю, что мьютекс является глобальным, но что происходит с тем мьютексом, который вызывает зависание программы?

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

Как я могу определить, находится ли имя mutex_name в плохом состоянии, поэтому пришло время вызвать удаление для него?

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

...