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();
}

效果:
Qt 只运行一个程序实例 -QLockFile -QSystemSemaphore 和 QSharedMemory_第1张图片

使用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();
}

运行效果:
Qt 只运行一个程序实例 -QLockFile -QSystemSemaphore 和 QSharedMemory_第2张图片

程序源码

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/
https://blog.csdn.net/y396397735/article/details/80814497

你可能感兴趣的:(QT)