【问题标题】:QSharedMemory::create() issueQSharedMemory::create() 问题
【发布时间】:2014-11-02 21:19:28
【问题描述】:

我正在尝试编写一个只允许运行一个程序实例的SingleApplication 类。我正在使用QSharedMemory

来实现它

程序运行良好,除非我使用值为"42" 的键。我做错了什么吗?这是未定义的行为吗?

Main.cpp

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

    //QApplication a(argc, argv);
    SingleApplication a(argc, argv, "42"); //Does not work with '42'. Will work for any other value. 



    MainWindow w;
    w.show();


    return a.exec();
}

SingleApplication.h

class SingleApplication : public QApplication
{
    Q_OBJECT

public:
    SingleApplication(int &argc, char *argv[], const QString uniqueKey);

    bool alreadyExists() const{ return bAlreadyExists; }

    bool isMasterApp() const { return !alreadyExists(); }

    bool sendMessage(const QString &message);

public slots:
    //void checkForMessages();

signals:
    //void messageAvailable(const QStringList& messages);

private:
    bool bAlreadyExists;
    QSharedMemory sharedMemory;

};

SingleApplication.cpp

SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv){

    sharedMemory.setKey(uniqueKey);

    //Create if one does not exist already
    if(sharedMemory.create(5000))
    {
        qDebug() << "Created!";

        bAlreadyExists = false;
    }
    else{
        if(sharedMemory.error() == QSharedMemory::AlreadyExists){
            qWarning() << "Program is already running!";
        }
    }
}

【问题讨论】:

  • 具体错误是什么?
  • @Oncaphillis sharedMemory.create() 只是返回 false。即使是第一次运行程序。
  • 共享内存段“42”是否已被另一个进程/线程使用?我现在才 shm 从 linux 和密钥只是不同进程可能相同的数字。但是,有些助手应该可以帮助您保持它们的独特性。尝试使用列出当前 shm 段的工具。
  • @EngieOP:errorString() 说什么?我不明白你的用例和实现。
  • 42 在整个银河系中全局分配 ;-)

标签: c++ qt shared-memory qtcore qsharedmemory


【解决方案1】:

我建议你下一个解决方案。它已经过测试,但它不支持在实例之间发送消息。它解决了您的解决方案的一些错误。因为仅仅测试内存是不够的。您需要保护共享内存的创建。

RunGuard.h

#ifndef RUNGUARD_H
#define RUNGUARD_H

#include <QObject>
#include <QSharedMemory>
#include <QSystemSemaphore>


class RunGuard
{

public:
    RunGuard( const QString& key );
    ~RunGuard();

    bool isAnotherRunning();
    bool tryToRun();
    void release();

private:
    const QString key;
    const QString memLockKey;
    const QString sharedmemKey;

    QSharedMemory sharedMem;
    QSystemSemaphore memLock;

    Q_DISABLE_COPY( RunGuard )
};


#endif // RUNGUARD_H

RunGuard.cpp

#include "RunGuard.h"

#include <QCryptographicHash>


namespace
{

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 )
    : key( key )
    , memLockKey( generateKeyHash( key, "_memLockKey" ) )
    , sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
    , sharedMem( sharedmemKey )
    , memLock( memLockKey, 1 )
{
        QSharedMemory fix( sharedmemKey );    // Fix for *nix: http://habrahabr.ru/post/173281/
        fix.attach();
}

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

bool RunGuard::isAnotherRunning()
{
    if ( sharedMem.isAttached() )
        return false;

    memLock.acquire();
    const bool isRunning = sharedMem.attach();
    if ( isRunning )
        sharedMem.detach();
    memLock.release();

    return isRunning;
}

bool RunGuard::tryToRun()
{
    if ( isAnotherRunning() )   // Extra check
        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();
}

【讨论】:

  • 据我了解,OP 对“幻数”感兴趣,而不是锁定,以及为什么这不起作用。锁定也不能解决创建问题,可能只是阻塞或继续(取决于使用的锁定功能),但在任何一种情况下,外部进程或线程仍将保留共享内存,而 OP 不会对其进行保留.
  • @lpapp 在这种情况下,他可以使用进程资源管理器之类的东西来查看他的共享内存的真实名称,并检查它是否很忙。对我来说,原来的问题听起来很奇怪。我只是指出错误并建议使用哈希来防止冲突。
  • 您可以使用 nativeKey() 方法,但我假设后台的某些东西保留了共享内存,除非 OP 犯了一些愚蠢的错误。 :) 如果是前者,以便用该字符串保留共享内存,那么不幸的是运气不好,Qt 对此无能为力。此外,OP 的概念将在没有共享内存的系统上被打破。
【解决方案2】:

我会放弃你个人从头开始实施的整个自己的单一应用程序概念。 qt-solutions repository 包含 QtSingleApplication 类,该类也被移植到 Qt 5。你应该可以使用它。在我看来,重新发明轮子几乎没有意义。

您是否仍然坚持自己做,虽然您的想法一开始似乎有点奇怪,将密钥传递给构造函数而不是在类内部透明地管理它,这可能是您的情况的一种解决方法使解决方案更加健壮:

SingleApplication::SingleApplication(int &argc, char *argv[], const QString uniqueKey) : QApplication(argc, argv)
{
    sharedMemory.setKey(uniqueKey);
    if (!sharedMemory.create(5000)) {
        while (sharedMemory.error() == QSharedMemory::AlreadyExists) {
            // Set a new key after some string manipulation
            // This just a silly example to have a placeholder here
            // Best would be to increment probably, and you could still use
            // a maximum number of iteration just in case.
            sharedMemory.setKey(sharedMemory.key() + QLatin1String("0"));
            // Try to create it again with the new key
            sharedMemory.create(5000);
        }
        if (sharedMemory.error() != QSharedMemory::NoError)
            qDebug() << "Could not create the shared memory:" << sharedMemory.errorString();
        else
        {
            qDebug() << "Created!";
            bAlreadyExists = false;
        }
    }

}

免责声明:这只是伪代码,实际上我从未测试过它,甚至没有尝试自己编译它!

【讨论】:

    猜你喜欢
    • 2018-03-24
    • 1970-01-01
    • 1970-01-01
    • 2020-04-14
    • 1970-01-01
    • 1970-01-01
    • 2015-11-29
    • 2020-08-12
    • 2020-09-20
    相关资源
    最近更新 更多