QT程序利用共享内存实现单例启动

1 实现原理

 

       程序在启动第一个实例时,会先尝试attach一块指定key的共享内存,由于这个共享内存事先并不存在,所以尝试attach失败。失败之后,第一个实例会create这块指定key的共享内存。

        当程序启动第二个实例时,依然会先尝试attach一块指定key的共享内存,由于这个共享内存刚被第一个实例创建,所以尝试attach返回true。于是得知,当前实例并非第一个实例,便不允许第二个实例继续启动。

        流程图如下:

​​​​​​​​​​​​​​

Ps:

       如果不考虑任何的共享内存残留或者sharedMem已经创建的情况,直接进行attach()会返回失败, 因为sharedMem还没有被create呢。

sharedMem还未被create的情况下,直接进行attach()会返回false。

        但考虑到某些特殊情况(已有实例启动)下,由于操作系统中存在已被创建的sharedMem,这时attach就会返回true。

        经过测试,QT程序异常奔溃(访问野指针)后,再重新启动实例sharedMem.attach()将返回false。 也就说,QT程序异常奔溃(访问野指针)后,系统将sharedMem作为资源回收了。

2 代码实现

2.1 头文件

runguard.h

#ifndef RUNGUARD_H
#define RUNGUARD_H


#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>
class RunGuard
{

public:
    explicit RunGuard(const QString& key);
    ~RunGuard();
    bool tryToRun();
    void release();

private:
    const QString memLockKey;
    const QString sharedmemKey;

    QSharedMemory sharedMem;
    QSystemSemaphore memLock;

    Q_DISABLE_COPY( RunGuard )
};


#endif // RUNGUARD_H

说明:

Q_DISABLE_COPY宏作用是禁止对给定的类使用复制构造函数和赋值运算符。

Q_DISABLE_COPY作用:

「Q_DISABLE_COPY宏作用是禁止对给定的类使用复制构造函数和赋值运算符。」 Q_DISABLE_COPY宏多使用在QObject类或其派生类中。如果我们需要禁止用户复制或赋值类对象,在类内使用Q_DISABLE_COPY宏即可。

  如果某些类内有指针成员变量,那么在复制或赋值很容易时候就会出现浅拷贝问题,当「复制类」的指针成员变量被释放时,「原类」内的指针成员变量再次释放就会导致程序意向不到的后果(二次析构)。

2.2 实现文件

runguard.cpp

#include "runguard.h"
#include <QCryptographicHash>

//椒盐哈希
QString generateKeyHash( const QString& key, const QString& salt )
{
    QByteArray data;

    data.append( key.toUtf8() );
    data.append( salt.toUtf8() );
    data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();

    return data;
}


//构造参数列表
RunGuard::RunGuard(const QString& key) :
                    memLockKey( generateKeyHash( key, "_memLockKey" ) ),
                    sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) ),
                    sharedMem( sharedmemKey ),
                    memLock( memLockKey, 1 )
{

}

RunGuard::~RunGuard()
{
    release();
}


//返回false说明已经有实例在运行了,不能启动额外的实例。
bool RunGuard::tryToRun()
{
    /*@ sharedMem.attach():
     * Returns true if the attach operation is successful.
     * If false is returned, call error() to determine which error occurred.
     * After attaching the shared memory segment, a pointer to the shared memory
     * can be obtained by calling data().
    */
    //如果不考虑任何的共享内存残留或者sharedMem已经创建的情况,直接进行attach()会返回失败,
    //因为sharedMem还没有被create呢。
    //sharedMem还未被create的情况下,直接进行attach()会返回false。
    //但考虑到某些特殊情况已有实例启动)下,由于操作系统中存在已被创建的sharedMem,这时attach就会返回true.
    //经过测试,QT程序异常奔溃(访问野指针)后,再重新启动实例sharedMem.attach()将返回false。
    //也就说,QT程序异常奔溃(访问野指针)后,系统将sharedMem作为资源回收了。
    memLock.acquire();
    const bool isRunning = sharedMem.attach();
    //这时后要考虑将当前进程实例与sharedMem之间解耦(detach)。
    if ( isRunning )
        sharedMem.detach();
    memLock.release();

    if(isRunning == true)//已有实例启动
        return false;

    memLock.acquire();
    const bool result = sharedMem.create( sizeof( quint64 ) );//创建共享内存
    memLock.release();
    if ( !result )
    {
        release();
        return false;
    }
    return true;
}

void RunGuard::release()
{
    memLock.acquire();
    if ( sharedMem.isAttached() )
        sharedMem.detach();
    memLock.release();
}

3 使用范例

Supongo que te gusta

Origin blog.csdn.net/thequitesunshine007/article/details/119610574
Recomendado
Clasificación