Qt 只運行一個程序實例 -QLockFile -QSystemSemaphore 和 QSharedMemory
來源 https://blog.csdn.net/y396397735/article/details/80814497
前言
每次只運行應用程序的一個實例可能是必要的,以限制內存泄漏的問題,或者消除某些資源,文件,SQLite數據庫等應用程序的兩個實例之間的競爭問題。或者,原則上,應用程序只需要用戶使用一個副本就行了。
有兩種方法可以用來解決這個問題:
1、使用QLockFile
當一個臨時文件被創建時,當應用程序關閉時清除這個臨時文件。因此,在應用程序的第二個實例啟動時檢查該文件是否已經創建了一個打開的應用程序實例,如果這個文件存在那么第二個就可以不啟動了。
2、使用QSystemSemaphore和QSharedMemory
這種通過創建一個共享內存段,並嘗試將其連接到具有唯一標識符的現有段。如果連接嘗試成功,則表明應用程序的一個實例已經創建。因此,我們通知用戶並關閉應用程序。如果連接嘗試不成功,那么我們為應用程序選擇創建這個內存段並運行第一個實例。
QLockFile
在應用程序啟動期間,創建一個臨時“鎖定文件”,如果嘗試創建鎖定文件不成功,則程序表明已經打開應用程序的一個實例,通知用戶並關閉當前未啟動的實例。
測試程序:
int main(int argc, char *argv[]) { QApplication a(argc, argv); // 本測試程序id取名為SingleAppTest QString path = QDir::temp().absoluteFilePath("SingleAppTest.lock"); // path = C:/Users/yu/AppData/Local/Temp/SingleAppTest.lock QLockFile lockFile(path); bool isLock = lockFile.isLocked(); // bool QLockFile::tryLock(int timeout = 0) // tryLock嘗試創建鎖定文件。此函數如果獲得鎖,則返回true; 否則返回false。 // 如果另一個進程(或另一個線程)已經創建了鎖文件,則此函數將最多等待timeout毫秒 if (!lockFile.tryLock(100)) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setText("The application is already running.\n" "Allowed to run only one instance of the application."); msgBox.exec(); return 1; } SingleAppTest w; w.setFixedSize(250, 150); w.show(); return a.exec(); }
效果:
使用QSystemSemaphore 和 QSharedMemory
在上面的例子中,通過限制Qt應用程序運行實例的數量,給出了一個簡單而方便的解決方案。
但是,在涉及用戶權限方面場景時可能會有一些缺點。你想為整個計算機運行一個單個實例,並且多用戶可以運行它,那么用QLockFile就提供不了這個能力了。
QSharedMemory則相反,在計算機上工作的同時,所有用戶共享。因此,如果你的任何用戶先運行程序,后者將無法運行它。但是在這種情況下,有必要考慮不同平台共享內存的差異。
在Windows的情況下,共享內存將在程序正常完成時釋放,並在突發情況下也能回收。在Linux/UNIX的情況下發生突發情況時崩潰后內存將無法釋放。
在下面的代碼中,信號量用於在同時啟動同一應用程序的多個實例的情況下解決競爭問題。
信號量由計數器創建,其最大數量為1.
當引發信號量時,應用程序的所有其他實例不再訪問共享內存,因此一個實例完全擁有資源。此實例通過存在帶有與此應用程序匹配的標識符的共享內存段來檢查應用程序的另一個運行實例。該實例成功啟動並創建共享內存段,以防它找不到關於該應用程序的另一個實例的信息。之后,信號量被丟棄,允許應用程序的其他實例嘗試啟動。
int main(int argc, char *argv[]) { QApplication a(argc, argv); // 創建信號量 QSystemSemaphore semaphore("SingleAppTest2Semaphore", 1); // 啟用信號量,禁止其他實例通過共享內存一起工作 semaphore.acquire(); #ifndef Q_OS_WIN32 // 在linux / unix 程序異常結束共享內存不會回收 // 在這里需要提供釋放內存的接口,就是在程序運行的時候如果有這段內存 先清除掉 QSharedMemory nix_fix_shared_memory("SingleAppTest2"); if (nix_fix_shared_memory.attach()) { nix_fix_shared_memory.detach(); } #endif // 創建一個共享內存 “SingleAppTest2”表示一段內存的標識key 可作為唯一程序的標識 QSharedMemory sharedMemory("SingleAppTest2"); // 測試是否已經運行 bool isRunning = false; // 試圖將共享內存的副本附加到現有的段中。 if (sharedMemory.attach()) { // 如果成功,則確定已經存在運行實例 isRunning = true; } else { // 否則申請一字節內存 sharedMemory.create(1); // 確定不存在運行實例 isRunning = false; } semaphore.release(); // 如果您已經運行了應用程序的一個實例,那么我們將通知用戶。 if (isRunning) { QMessageBox msgBox; msgBox.setIcon(QMessageBox::Warning); msgBox.setText("The application is already running.\n" "Allowed to run only one instance of the application."); msgBox.exec(); return 1; } SingleAppTest2 w; w.setFixedSize(250, 150); w.show(); return a.exec(); }
運行效果:
程序源碼
https://github.com/lesliefish/Qt/tree/master/Project/QtGuiApplication/SingleAppTest https://github.com/lesliefish/Qt/tree/master/Project/QtGuiApplication/SingleAppTest2
原文參考:
https://evileg.com/ru/post/147/
============= End