Тек бір қолданба данасын іске қосуға рұқсат беру жадтың ағып кетуіне қатысты мәселелерді шектеу немесе бірдей ресурстар, файлдар, SQLite дерекқоры және т.б. үшін бәсекелес екі қолданба даналарымен ықтимал ақауларды жою үшін қажет болуы мүмкін. Немесе қолданба, негізінен, пайдаланушы пайдаланатын бір ғана дананы қабылдайтын болса.
Бұл мәселені шешу үшін келесі екі әдісті қолдануға болады:
- QLockFile пайдалану - қолданба жабылған кезде жойылатын уақытша файл жасалғанда. Бұл ретте қосымшаның екінші данасы іске қосылған кезде осы файлдың бар-жоғына тексеру жүргізіледі және егер файл қолданбаның бір ашық данасы арқылы жасалған болса, онда екінші данасы автоматты түрде жабылады;
- QSystemSemaphore және QSharedMemory пайдалану - бұл жағдайда ортақ жад сегменті жасалады және оны бірегей идентификатор арқылы бар сегментке қосу әрекеті жасалады. Тіркеме әрекеті сәтті болса, бір қолданба данасы әлдеқашан жасалған. Тиісінше, біз бұл туралы пайдаланушыға хабарлаймыз және қолданбаны жабамыз. Тіркеу әрекеті сәтсіз болса, біз қолданба үшін жад сегментін жасаймыз және бірінші дананы іске қосамыз.
Бағдарламаның барлық келесі коды main.cpp шегінен шықпайды.
QLockFile қолданатын жалғыз қолданба
Төмендегі кодта қолданба іске қосылған кезде уақытша файлдар қалтасында Lock File жасалады; Lock File құру әрекеті сәтсіз болған жағдайда, бағдарлама қолданбаның бір данасы деп есептейді. әлдеқашан ашық, ол туралы пайдаланушыға хабарлайды және жабылады.
Ескерту. Ауыстыру
#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(); }
QSystemSemaphore және QSharedMemory көмегімен жалғыз қолданба
Бірінші нұсқа Qt қолданбасының іске қосылған даналарының санын шектеу мәселесінің қарапайым және ыңғайлы шешімін береді. Бірақ QLockFile шешімінің кемшіліктері де бар, себебі пайдаланушы үшін рұқсат мәселелері болуы мүмкін. Сондай-ақ, бағдарламаны бүкіл компьютерде бір данада іске қосуды шектегіңіз келсе, онда қанша пайдаланушы сеансы іске қосылатынына қарамастан, QLockFile пайдалану да бұл мүмкіндікті қамтамасыз етпейді.
QSharedMemory , керісінше, компьютерде бір уақытта жұмыс істейтін барлық пайдаланушыларға ортақ. Сондықтан, егер пайдаланушылардың бірі сіздің бағдарламаңызды іске қосса, екіншісі оны енді іске қоса алмайды.
Бірақ бұл жағдайда әртүрлі платформаларда ортақ жадпен жұмыс істеудегі айырмашылықтар туралы ұмытпау керек. Windows, жағдайында ортақ жад бағдарламаның қалыпты аяқталуы кезінде де, әдеттен тыс аяқталғанда да босатылады. Linux/Unix жағдайында, бұзылған кезде жад босатылмайды.
Төмендегі кодта бір қолданбаның бірнеше даналары бір уақытта іске қосылған кезде жарыс шартын шешу үшін семафор пайдаланылады. Семафор есептегішпен жасалады, оның ең көп саны 1. Семафор көтерілген кезде қолданбаның барлық басқа даналары ортақ жадқа енді қатынаса алмайды және сәйкесінше бір данасы ресурстарға толық иелік етеді. Бұл дана қолданбаға сәйкес идентификаторы бар ортақ жад сегментінің болуы арқылы іске қосылған қолданбаның басқа данасын тексереді. Дана сәтті басталады және басқа қолданба данасы туралы ақпаратты таппаған жағдайда ортақ жад сегментін жасайды. Содан кейін семафор жойылып, қолданбаның басқа даналарына бастауға әрекет жасауға мүмкіндік береді.
Ескерту. Ауыстыру
#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(); }
День добрый.
Подскажите, как с помощью этих примеров вместо вывода сообщения сделать активным окно запущенного экземпляра приложения?
Добрый день!
С помощью API целевой системы придётся найти нужный процесс и открыть окно. Но придётся писать платформозависимый код. В случае Windows нужно смотреть, как с помощью WinAPI искать процессы и пытаться открыть их окна, если они есть. В случае Linux там ещё сложнее, или через XLib, или через DBus, но я не уверен, как именно это делается.
Понятно. Спасибо!