Реклама

Qt/C++ - Урок 043. Qt Single Application - запускаем только один экземпляр приложения

QLockFile, QSharedMemory, QSystemSemaphore, Qt, Qt Single Application

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

Для решения данной задачи можно использовать два следующих способа:

  • С использованием QLockFile - когда создаётся временный файл, который уничтожается при закрытии приложения. При этом при запуске второго экземпляра приложения происходит проверка на существование данного файла и если файл уже создан одним открытым экземпляром приложения, то второй экземпляр автоматически закрывается;
  • С использованием QSystemSemaphore и QSharedMemory - в данном случае создаётся сегмент разделяемой памяти и производится попытка присоединить его к существующему сегменту по уникальному идентификатору. Если попытка присоединения прошла успешно, значит один экземпляр приложения уже создан. Соответственно, сообщаем об этом пользователю и закрываем приложение. В случае, если попытка присоединения прошла безуспешно, то создаём выделяем сегмент памяти для приложения и запускаем первый экземпляр.

Весь дальнейший программный код не будет выходить за пределы main.cpp.

Single application с использованием QLockFile

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

Примечание. Замените <uniq id> на ваш идентификатор.

#include "widget.h"
#include <QApplication>
#include <QLockFile>
#include <QDir>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QLockFile lockFile(QDir::temp().absoluteFilePath("lurity.lock"));

    /* Пытаемся закрыть Lock File, если попытка безуспешна в течение 100 миллисекунд,
     * значит уже существует Lock File созданный другим процессом.
     * Следовательно, выбрасываем предупреждение и закрываем программу
     * */
    if(!lockFile.tryLock(100)){
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setText("Приложение уже запущено.\n"
                       "Разрешено запускать только один экземпляр приложения.");
        msgBox.exec();
        return 1;
    }

    Widget w;
    w.show();

    return a.exec();
}

Single application с использованием QSystemSemaphore и QSharedMemory

В первом варианте дано простое и удобное решение задачи по ограничению количества запущенных экземпляров Qt приложения. Но решение на QLockFile имеет и недостатки в том плане, что могут быть проблемы с разрешениями для пользователя. А также, если вы желаете ограничить, запуск программы одним экземпляром на весь компьютер, не различия то, сколько пользовательских сессий может быть запущено на нём, то использование QLockFile также не обеспечит эту возможность.

QSharedMemory же напротив является разделяемой для всех пользователей одновременно, работающих за компьютером. Поэтому, если один из пользователей запустил Вашу программу, то второй уже не сможет её запустить.

Но в данном варианте необходимо не забывать о различиях в работе с разделяемой памятью под различными платформами. В случае с Windows, разделяемая память будет освобождена как при штатном завершении программы, так и при аварийном завершении. В случае же с Linux/Unix при аварийном при аварийном завершении память освобождена не будет.

В ниже представленном коде семафор используется для разрешения проблемы гонок в случае с одновременным запуском нескольких экземпляров одного приложения. Семафор создаётся со счётчиком, максимальное число которого равно 1. При поднятии семафора, все другие экземпляры приложения уже не имеют доступа к разделяемой памяти и соответственно один экземпляр полностью владеет ресурсами. Данный экземпляр проверяет наличие запущенного другого экземпляра приложения по наличию сегмента разделяемой памяти с идентификатором, соответствующим данному приложению. Экземпляр успешно запускается и создаёт сегмент разделяемой памяти в случае, если не нашёл информации о другом экземпляре приложения. После этого семафор опускается, давая возможность другим экземплярам приложения попытаться запуститься.

Примечание. Замените <uniq id> и <uniq id 2> на ваши идентификатор.

#include "mainwindow.h"
#include <QApplication>
#include <QSystemSemaphore>
#include <QSharedMemory>
#include <QMessageBox>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QSystemSemaphore semaphore("<uniq id>", 1);  // создаём семафор
    semaphore.acquire(); // Поднимаем семафор, запрещая другим экземплярам работать с разделяемой памятью

#ifndef Q_OS_WIN32
    // в linux/unix разделяемая память не освобождается при аварийном завершении приложения,
    // поэтому необходимо избавиться от данного мусора
    QSharedMemory nix_fix_shared_memory("<uniq id 2>");
    if(nix_fix_shared_memory.attach()){
        nix_fix_shared_memory.detach();
    }
#endif

    QSharedMemory sharedMemory("<uniq id 2>");  // Создаём экземпляр разделяемой памяти
    bool is_running;            // переменную для проверки ууже запущенного приложения
    if (sharedMemory.attach()){ // пытаемся присоединить экземпляр разделяемой памяти
                                // к уже существующему сегменту
        is_running = true;      // Если успешно, то определяем, что уже есть запущенный экземпляр
    }else{
        sharedMemory.create(1); // В противном случае выделяем 1 байт памяти
        is_running = false;     // И определяем, что других экземпляров не запущено
    }
    semaphore.release();        // Опускаем семафор

    // Если уже запущен один экземпляр приложения, то сообщаем об этом пользователю
    // и завершаем работу текущего экземпляра приложения
    if(is_running){
        QMessageBox msgBox;
        msgBox.setIcon(QMessageBox::Warning);
        msgBox.setText(QObject::trUtf8("Приложение уже запущено.\n"
                        "Вы можете запустить только один экземпляр приложения."));
        msgBox.exec();
        return 1;
    }

    MainWindow w;
    w.show();

    return a.exec();
}

Видеоурок

Реклама

Комментарии

Комментарии

Только авторизованные пользователи могут оставлять комментарии.
Пожалуйста, Авторизуйтесь или Зарегистрируйтесь
  • falcon
  • 16 января 2018 г. 17:25

Qt - Тест 001. Сигналы и слоты

  • Результат 100 баллов
  • Очки рейтинга 10
  • falcon
  • 16 января 2018 г. 17:22

Qt - Тест 001. Сигналы и слоты

  • Результат 68 баллов
  • Очки рейтинга -1
  • falcon
  • 16 января 2018 г. 17:18

C++ - Тест 001. Первая программа и типы данных

  • Результат 73 баллов
  • Очки рейтинга 1
Последние комментарии

QML - Урок 021. Переключение между окнами в QML

Спасибо всем. Все получилось. Прикручиваю логику.

  • BlinCT
  • 14 января 2018 г. 19:28

Разработка на Qt под iOS

Вот честно, на сколько же муторно под огрызок что то делать. Куча проблем) А вод линь или под Андроид все просто и тривиально))

  • folax
  • 12 января 2018 г. 9:16

QML - Урок 021. Переключение между окнами в QML

Ничего сложного, делаете по тех заданию 3 файла qml, называете их как указанно в тех задании, потом из первого окна через Loader их переключаете, в окне 2 и 3 делаете сигналы которые при закры...

QML - Урок 021. Переключение между окнами в QML

Все верно, я и не говорил что этот кусок кода лично мое произведение. Это тоже верно: Это задание для прохождения на собеседование в одну из крупных украинских IT компаний. Логику ...

  • folax
  • 12 января 2018 г. 8:13

QML - Урок 021. Переключение между окнами в QML

int main(int argc, char *argv[]){ QApplication app(argc, argv); Logic logic; QQmlApplicationEngine engine; engine.rootContext()->setContextProperty("logic", &logic)...

Сейчас обсуждают на форуме

ChartView. Отображение метки данных точки серии при наведении курсора

Спасибо большущее за советы! Все получилось через ScatterSeries. Методы remove() как-то сходу не дались, удаляет в первый раз, а потом программа падает... Не стал тратить время и воспользовалс...

  • EVILEG
  • 16 января 2018 г. 14:23

Как проверить доступность сервера

Добрый день! Теоретически можно использовать QTcpSocket, у него есть метод connectToHost. Возможно, что проверка доступности через этот класс будет осуществляться несколько быстрее,...

QGraphicsScene

спасибо, за подробное объяснение строчки, а с зумом я разобрался, все работает

  • EVILEG
  • 15 января 2018 г. 17:21

Qt webgl

Насчёт проверки подключения клиента я не в курсе. Что касается экземпляров приложения, то из того, что я читал получается, что нет необходимости в нескольких экземплярах для нескольких кл...

  • EVILEG
  • 15 января 2018 г. 11:39

Проблема добавления #DEFINE при сборке CMak'ом

А Вы не пробовали сделать предкомпилированные библиотеки boost под свою систему, а потом уже подключать собранные библиотеки Boost`а? Просто один только boost может собираться на пару гиг...