Qt: передовой опыт для защиты приложений с одним экземпляром

QSingleApplication? QMutex? QSharedMemory? Я ищу что-то, что будет гладко работать в Windows, OSX и Linux (Ubuntu). Использование Qt 4.7.1


person JasonGenX    schedule 15.02.2011    source источник
comment
stackoverflow.com/questions/783212 /   -  person Martin Beckett    schedule 15.02.2011
comment
ссылки там ведут на какой-то мобильный API с функциями камеры, роуминга и т. д. Вы уверены, что именно здесь вы получаете QtSingleApplication?   -  person JasonGenX    schedule 15.02.2011
comment
@Ссылка в ответе правильная doc.trolltech.com/solutions/4/ qtsingleapplication/, ссылка QtSingleApplication в вопросе неверна.   -  person Martin Beckett    schedule 15.02.2011


Ответы (7)


Простое решение, которое делает то, что вы хотите. Без зависимости от сети (как QtSingleApplication) и без каких-либо накладных расходов.

Применение:

int main()
{
    RunGuard guard( "some_random_key" );
    if ( !guard.tryToRun() )
        return 0;

    QAppplication a(/*...*/);
    // ...
}

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 )
{
    memLock.acquire();
    {
        QSharedMemory fix( sharedmemKey );    // Fix for *nix: http://habrahabr.ru/post/173281/
        fix.attach();
    }
    memLock.release();
}

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();
}
person Dmitry Sazonov    schedule 27.01.2015
comment
Это работает для меня, если приложение выйдет из строя, оно позволит перезапустить (это была моя проблема), спасибо @SaZ - person Haris; 18.02.2015
comment
@SaZ спасибо за публикацию этого кода - я только что попробовал его в своем приложении - он работает с первого раза :) - person Michael Vincent; 13.05.2015
comment
Вот это да. Работал отлично! Большое спасибо :) Просто нужно было включить RunGuard.h в main. - person mrg95; 01.09.2015
comment
Можно ли также сообщить что-то другому работающему экземпляру? Например, когда пользователь пытается открыть файл, но другой экземпляр приложения уже запущен, я хочу, чтобы этот файл был открыт существующим экземпляром. А что это за fix в конструкторе? - person bweber; 24.10.2015
comment
Другой вопрос: не могли бы вы просто использовать QSharedMemory::lock() вместо отдельного QSystemSemaphore? - person bweber; 24.10.2015
comment
@ user1488118 1) вы говорите о IPC, это не связано с текущим вопросом. QtSingleApplication может общаться. Или вы можете создать свой механизм на основе QLocalSocket. 2) Нет, QSharedMemory::lock() мало, но нужна длинная тема, чтобы описать, почему. - person Dmitry Sazonov; 26.10.2015
comment
Круто, какая лицензия у вашего фрагмента? Могу ли я использовать его в своей штуковине GPLv2 с этим заголовком: /882600 * Используется с разрешения автора. * / ?? - person Tomas Pruzina; 15.08.2016
comment
@AoeAoe это пример кода, ограничений нет. Не стесняйтесь использовать его даже без каких-либо замечаний. - person Dmitry Sazonov; 15.08.2016
comment
В runguard.obj есть LNK4042: object specified more than once; extras ignored предупреждение. Любая идея, почему и как его удалить? - person Mustafa Chelik; 07.09.2016
comment
@MustafaChelik это предупреждение компоновщика. Это не может быть связано с файлом .obj. Используйте подробный вывод компоновщика для глубокой диагностики. - person Dmitry Sazonov; 08.09.2016
comment
Вы правы Дмитрий. После run qmake и ребилда пошло предупреждение. Спасибо. - person Mustafa Chelik; 08.09.2016
comment
Почему именно вы используете хэш для ключей? - person xxmicloxx; 02.01.2017
comment
@xxmicloxx не имеет значения, что использовать. Я использую его для предотвращения возможных сбоев с другими глобальными объектами ядра. - person Dmitry Sazonov; 02.01.2017
comment
Некоторые спрашивали (и я задавался вопросом), почему бы просто не использовать QSharedMemory::lock() и не возиться с системным семафором. Я думаю, это связано с тем, что в системе может быть несколько пользователей. Общая память доступна для всех пользователей, поэтому нам нужен системный семафор, чтобы обеспечить доступ к ней только одного пользователя в каждый момент времени. Эта статья довольно хорошо объясняет это: blog.aeguana.com/2015/10/15/ - person sidewinderguy; 07.06.2017
comment
у меня не работает с Windows 10 и Qt 5.5. он блокирует несколько экземпляров с одним и тем же входом в Windows, но не с несколькими входами в Windows. - person Patrick Parker; 15.04.2019
comment
@PatrickParker Я думаю, что невозможно ответить на ваш вопрос с помощью кросс-платформенных решений или только кода Qt. Я могу порекомендовать вам использовать вещи, специфичные для платформы. Есть ссылка на статью (на русском языке), которая решит ваш вопрос: RSDN. Образцы кода можно взять отсюда или воспользоваться переводчиком (статья очень простая). Или вы можете задать свой вопрос с тегом WinAPI. - person Dmitry Sazonov; 16.04.2019
comment
@DmitrySazonov спасибо за ссылку. Я рассматривал возможность использования глобального мьютекса Windows, в конце концов я выбрал блокировку CreateFile, аналогичную приведенной здесь: /7098259, так как я забочусь только о предотвращении экземпляров, которые будут писать в одну и ту же папку ресурсов. - person Patrick Parker; 17.04.2019
comment
@PatrickParker к вашему сведению. Но будьте осторожны с ситуациями, когда приложение может дать сбой, а файл блокировки может остаться в живых. - person Dmitry Sazonov; 17.04.2019
comment
Жаль, что QT не имеет надежной реализации для QLockFile: это несколько бесполезно, если вам нужно хранить его бесконечно долго без проверки устаревания; потому что он легко становится осиротевшим, если программа аварийно завершает работу. Ручки из CreateFile как минимум надежно закрываются ОС. - person Patrick Parker; 17.04.2019
comment
@PatrickParker Я думаю, что это не проблема Qt. Очень сложно реализовать такие вещи для всех платформ, поддерживаемых Qt. Так что в вашем случае лучше использовать код для конкретной платформы. - person Dmitry Sazonov; 18.04.2019
comment
Обязательно ли хешировать ключи? У меня это работает даже без их хеширования или я что-то упускаю? - person Mahesh Jagtap; 14.04.2020
comment
@MaheshJagtap нет, это не так. Вы можете сгенерировать любую строку. Но лучший способ - иметь уникальную строку. Если другое приложение будет использовать объект ядра с таким же именем, вы не сможете запустить свое приложение. Он будет думать, что это уже началось. В любом случае, хеширование делается только один раз, на производительность это никак не должно влиять. Ресурсов для запуска процесса гораздо больше. - person Dmitry Sazonov; 14.04.2020
comment
Это потрясающая реализация! Большое спасибо, как раз то, что мне было нужно! - person Lorenzo_g; 03.08.2020

Поскольку QtSingleApplication относительно устарел и больше не поддерживается, я написал замену под названием SingleApplication.

Он основан на QSharedMemory и использует QLocalServer для уведомления родительского процесса о порождении нового экземпляра. Он работает на всех платформах и совместим с Qt 5.

Полный код и документация доступны здесь.

Основной пример:

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

    return app.exec();
}

Расширенный пример

Помимо прочего, он поддерживает отправку сообщений между вновь порожденным экземпляром и основным экземпляром, например:

int main(int argc, char *argv[])
{
    SingleApplication app( argc, argv, true );

    if( app.isSecondary() ) {
        app.sendMessage(  app.arguments().join(' ')).toUtf8() );
        app.exit( 0 );
    }

    return app.exec();
}
person Itay Grudev    schedule 14.01.2016
comment
Спасибо, что опубликовали это. Вы были бы открыты для запроса на включение, который добавляет команду для отправки с соединением? Я хотел бы дополнительно выполнить команду, например, открыть файл. - person Sohail; 03.08.2016
comment
signal( SIGILL, SingleApplicationPrivate::terminate ); // 4 LOL, ты хоть пробовал? Я полагаю, нет. Эта штука не работает. - person LtWorf; 09.08.2016
comment
@LtWorf В чем именно здесь проблема? - person Itay Grudev; 09.08.2016
comment
Ах, извините, я неправильно понял это как SIGKILL. В любом случае он сломан, потому что в случае sigkill он не выполняет никакой очистки. - person LtWorf; 09.08.2016
comment
Я знаю, что не могу справиться с SIGKILL, но я использовал другой хак, чтобы справиться с этим случаем. Инициализируя, а затем явно удаляя экземпляр QSharedMemory, ядро ​​очищает блок, если к нему не подключены какие-либо активные процессы. - person Itay Grudev; 09.08.2016
comment

Вы можете использовать QSharedMemory с определенным ключом и проверить, может ли быть создана общая память с этим ключом или нет. Если он не может его создать, значит экземпляр уже запущен:

QSharedMemory sharedMemory;
sharedMemory.setKey("MyApplicationKey");

if (!sharedMemory.create(1))
{
    QMessageBox::warning(this, tr("Warning!"), tr("An instance of this application is running!") );

    exit(0); // Exit already a process running
}
person Nejat    schedule 22.11.2014
comment
Решение не завершено. Возможных глюков очень много. Вы не проверяете случаи, когда приложение падает. Вы можете проверить мой ответ полным решением. - person Dmitry Sazonov; 27.01.2015
comment
Это решение действительно ужасно неполное. -1 - person TimZaman; 14.11.2015

для Windows:

HANDLE g_app_mutex = NULL;

bool check_one_app_instance()
{
    g_app_mutex = ::CreateMutex(NULL, FALSE, L"8BD290769B404A7816985M9E505CF9AD64"); // this any different key as string
    if(GetLastError() == ERROR_ALREADY_EXISTS)
    {
        CloseHandle(g_app_mutex);
        return false;
    }

    return true;
}
person andreyDev    schedule 28.02.2016
comment
Вопрос касается кроссплатформенного решения. - person Dmitry Sazonov; 14.03.2016

Я использую это решение на данный момент.

Однако у него есть недостаток, заключающийся в том, что программа может быть запущена пользователем только один раз, даже если он входит в систему из нескольких мест одновременно.

одиночный экземпляр.h

#ifndef SINGLEINSTANCE_H
#define SINGLEINSTANCE_H

typedef enum {
    SYSTEM,
    SESSION,
} scope_t;

class SingleInstance
{
public:
    static bool unique(QString key, scope_t scope);
};

#endif // SINGLEINSTANCE_H

singleinstance.cpp

#include <QLockFile>
#include <QProcessEnvironment>

#include "singleinstance.h"

/**
 * @brief filename
 * @param key
 * @param scope
 * @return a fully qualified filename
 *
 * Generates an appropriate filename for the lock
 */
static QString filename(QString key, scope_t scope) {

    QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
    QString tmp = env.value("TEMP", "/tmp") + "/";
    QString user = env.value("USER", "alfio");


    QString r;                                                                                                                                                                         
    switch (scope) {                                                                                                                                                                   
        case SYSTEM:                                                                                                                                                                   
            r = tmp;                                                                                                                                                                   
            break;
        case SESSION:
            //FIXME this will prevent trabucco to run in multiple X11 sessions
            r = env.value("XDG_RUNTIME_DIR", tmp + user) + "/";
            break;
    }
    return r + key + ".lock";
}

/**
 * @brief SingleInstance::unique
 * @param key the unique name of the program
 * @param scope wether it needs to be system-wide or session-wide
 * @return true if this is the only instance
 *
 * Make sure that this instance is unique.
 */
bool SingleInstance::unique(QString key, scope_t scope) {
    QLockFile* lock = new QLockFile(filename(key, scope));
    bool r = lock->tryLock();
    if (!r)
        delete lock;
    return r;
}
person LtWorf    schedule 24.08.2016
comment
Что вы будете делать, если у вас нет прав на запись во временную папку? Также это хуже для производительности. - person Dmitry Sazonov; 24.08.2016
comment
В машине, которая работает правильно, у вас есть доступ к этому. Если у вас нет /tmp, вы даже не сможете войти в систему через графический интерфейс. Производительность почти не имеет значения, так как это проверка выполняется один раз при запуске программы. - person LtWorf; 24.08.2016
comment
Это зависит от вашей платформы. Я создал приложения для встроенной Windows XP с очень ограниченным доступом. Там, где во время выполнения была указана только одна доступная для записи папка для нужд приложения и путь к этой папке. Поэтому проверка заблокированных файлов была очень дорогой операцией. - person Dmitry Sazonov; 24.08.2016
comment
кто является владельцем блокировки QLockFile*? похоже, это висящий ресурс - person Patrick Parker; 16.04.2019

для линукса:

//----------------------------------

QProcess *m_prSystemCall;
m_prSystemCall = new QProcess();

QString Commnd = "pgrep  " + qApp->applicationDisplayName();
m_prSystemCall->start(Commnd);
m_prSystemCall->waitForFinished(8000);
QString output(m_prSystemCall->readAllStandardOutput());
QStringList AppList = output.split("\n", QString::SkipEmptyParts);
qDebug() <<"pgrep out:"<<AppList;
for(int i=0;i<AppList.size()-1;i++)
{
    Commnd = "kill " + AppList.at(i);
    m_prSystemCall->start(Commnd);
    m_prSystemCall->waitForFinished(8000);
}

//-------------------------------------------------------

и для Windows:

#include <tlhelp32.h>
#include <comdef.h>

QString pName = qApp->applicationDisplayName();
pName += ".exe";
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);

HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);

if (Process32First(snapshot, &entry) == TRUE)
{
    DWORD myPID =  GetCurrentProcessId();
    while (Process32Next(snapshot, &entry) == TRUE)
    {
        const WCHAR* wc = entry.szExeFile ;
        _bstr_t b(wc);
        const char* c = b;

        if (stricmp(c, pName.toStdString().c_str()) == 0)
        {
            HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID);

            qDebug() <<"myPID: "<< myPID << "entry.th32ProcessID" << entry.th32ProcessID;
            if(myPID != entry.th32ProcessID)
                TerminateProcess(hProcess,0);
            QThread::msleep(10);
            CloseHandle(hProcess);
        }
    }

}

CloseHandle(snapshot);
person aminM    schedule 25.09.2017

Согласно документу Qt, полученный QSystemSemaphore не будет автоматически освобожден, если процесс выйдет из строя без вызова его деструктора в Unix-подобных ОС. Это может быть причиной взаимоблокировки в другом процессе, пытающемся получить тот же семафор. Если вы хотите быть на 100 % уверены, что ваша программа правильно обрабатывает сбои, и если вы не настаиваете на использовании Qt, вы можете захотеть использовать другие механизмы блокировки, которые операционные системы автоматически освобождают при завершении процесса, например, lockf() и флаг O_EXLOCK, переданный в open(), которые упоминаются в Как восстановить семафор, когда процесс, который уменьшил его до нуля, дает сбой? или flock(). Фактически создание разделяемой памяти больше не требуется, если используется flock(). Простого использования flock() достаточно для защиты одного экземпляра приложения.

Если восстановление семафора после сбоев в Unix не имеет значения, я думаю, что RunGuard из ответа Дмитрия Сазонова можно было бы несколько упростить:

  1. Деструкторы ~RunGuard() и RunGuard::release() могут быть удалены, так как QSharedMemory автоматически отсоединится от сегмента разделяемой памяти после его уничтожения, как в документе Qt для QSharedMemory::~QSharedMemory(): «Деструктор очищает ключ, что заставляет объект разделяемой памяти отсоединяться от лежащей в его основе разделяемой памяти. сегмент.».

  2. RunGuard::isAnotherRunning() тоже можно снять. Цель - эксклюзивное исполнение. Как уже упоминал @Nejat, мы можем просто воспользоваться тем фактом, что для данного ключа в любое время может быть создано не более одного сегмента общей памяти, как в документе Qt для QSharedMemory::create(): «Если сегмент общей памяти идентифицируется ключом уже существует, операция присоединения не выполняется и возвращается false."

  3. Если я правильно понимаю, цель «исправить» объект QSharedMemory в конструкторе состоит в том, чтобы уничтожить сегмент разделяемой памяти, который выживает из-за сбоя предыдущего процесса, как в документе Qt: «Unix: ... Когда последний поток или процесс, который имеет экземпляр QSharedMemory, присоединенный к конкретному сегменту разделяемой памяти, отсоединяется от сегмента, уничтожая свой экземпляр QSharedMemory, ядро ​​Unix освобождает сегмент разделяемой памяти. выживает в аварии». Когда "fix" уничтожается, его деструктор должен вызвать неявный detach(), и выживший сегмент разделяемой памяти, если таковой имеется, будет освобожден.

  4. Не уверен, является ли QSharedMemory потокобезопасным/процессобезопасным или нет. В противном случае код, относящийся к memLock, может быть дополнительно удален, если потокобезопасность будет обрабатываться внутри QSharedMemory. С другой стороны, fix также должен быть защищен memLock, если безопасность является проблемой:

    RunGuard::RunGuard( const QString& key )
        : key( key )
        , memLockKey( generateKeyHash( key, "_memLockKey" ) )
        , sharedMemKey( generateKeyHash( key, "_sharedMemKey" ) )
        , sharedMem( sharedMemKey )
        , memLock( memLockKey, 1 )
    {
        memLock.acquire();
        {
            QSharedMemory fix( sharedMemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
            fix.attach();
        }
        memLock.release();
    }
    

    потому что явный attach() и неявный detach() вызываются вокруг fix.

  5. Упрощенная версия RunGuard выглядит следующим образом:

    Применение:

    int main()
    {
        RunGuard guard( "some_random_key" );
        if ( !guard.tryToRun() )
            return 0;
    
        QAppplication a(/*...*/);
        // ...
    }
    

    runGuard.h:

    #ifndef RUNGUARD_H
    #define RUNGUARD_H
    
    #include <QObject>
    #include <QSharedMemory>
    #include <QSystemSemaphore>
    
    class RunGuard
    {
    
    public:
        RunGuard( const QString& key );
        bool tryToRun();
    
    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();
    }
    
    bool RunGuard::tryToRun()
    {
        memLock.acquire();
        const bool result = sharedMem.create( sizeof( quint64 ) );
        memLock.release();
        if ( !result )
            return false;
    
        return true;
    }
    
  6. Здесь возможно состояние гонки:

    bool RunGuard::tryToRun()
    {
        if ( isAnotherRunning() )   // Extra check
            return false;
                                                                   // (tag1)
        memLock.acquire();
        const bool result = sharedMem.create( sizeof( quint64 ) ); // (tag2)
        memLock.release();
        if ( !result )
        {
            release();                                             // (tag3)
            return false;
        }
    
        return true;
    }
    

    Рассмотрим сценарий:

    Когда текущий процесс ProcCur запускается на (tag1), происходит следующее: (обратите внимание, что (tag1) находится вне защиты от блокировки)

    1. Another process ProcOther using RunGuard starts to run.
    2. ProcOther запускается на (tag2) и успешно создает общую память.
    3. ProcOther аварийно завершает работу до вызова release() по адресу (tag3).
    4. ProcCur продолжает работать с (tag1).
    5. ProcCur переходит к (tag2) и пытается создать общую память. Однако sharedMem.create() вернет false, потому что ProcOther оставил созданный. Как мы видим в документе QSharedMemory::create(): «Если сегмент разделяемой памяти, идентифицированный ключом, уже существует, операция присоединения не выполняется и возвращается false».
    6. Наконец, RunGuard::tryToRun() в ProcCur вернет false, что не так, как ожидалось, поскольку ProcCur — единственный существующий процесс, использующий RunGuard.
person Justin    schedule 25.10.2015
comment
1. Это необходимо в том случае, когда вы хотите сделать дополнительную логику и когда вам нужно напрямую выпустить RunGuard. 2. Эту проверку можно использовать и для других случаев, если требуется IPC. 3. Да, это фикс для *nix для вашего случая. 4. QSystemSemaphore используется как мьютекс между процессами. Необходимо защитить от некоторых условий гонки. Это не официант. RunGuard не является потокобезопасным, потому что потокобезопасность здесь не нужна. Но RunGuard должен быть безопасным. - person Dmitry Sazonov; 28.10.2015
comment
До сих пор неизвестно, является ли QSharedMemory безопасным для процессов/потоков или нет. Таким образом, защита вызовов функций-членов QSharedMemory с помощью QSystemSemaphore может быть тривиальной. Где-то должны быть упомянуты документы о том, что QSharedMemory не является безопасным для процессов/потоков, но я не могу найти ни один из таких документов. Следовательно, я сказал, что memLock МОЖЕТ быть удалена. - person Justin; 28.10.2015
comment
Согласен с защитой исправления. Но я думаю, что у вас есть некоторое непонимание того, что такое безопасность процессов и безопасность потоков. В RunGuard вообще не нужно заботиться о безопасности потоков. memLock используется для предотвращения некоторых гонок. - person Dmitry Sazonov; 28.10.2015
comment
Кстати, исправление тоже должно было быть защищено memLock, если бы QSharedMemory не был процессно-поточно-безопасным. Как я изменил 4. выше. - person Justin; 28.10.2015
comment
Согласно этому: i>реентерабельный, если его функции-члены можно безопасно вызывать из нескольких потоков, если каждый поток использует другой экземпляр класса. Класс является поточно-ориентированным, если его функции-члены можно безопасно вызывать из нескольких потоков, даже если все потоки используют один и тот же экземпляр класса. Если RunGuard предназначен для использования только один раз в каждом приложении, его объект QSharedMemory является единственным экземпляром, который отличается от экземпляра всех других RunGuard в других приложениях. - person Justin; 13.03.2016
comment
ты неправ. Вы говорите, что нет никакой разницы между потоконебезопасным и реентерабельным классом. В Qt reentrant означает, что вы можете использовать один экземпляр в разных потоках, но вы должны защищать доступ к объекту с помощью некоторых охранников. Вам необходимо улучшить свои навыки понимания многопоточности и понимания разницы между потоком и процессом. - person Dmitry Sazonov; 13.03.2016
comment
Кстати, вам нужно внимательно прочитать документацию. Прочтите еще раз: реентерабельную функцию также можно вызывать одновременно из нескольких потоков, но только в том случае, если каждый вызов использует свои собственные данные. О экземплярах ничего не сказано. Только о призыве. - person Dmitry Sazonov; 13.03.2016
comment
В коде возможна гонка. См. 6. выше. - person Justin; 13.03.2016
comment
@Justin 6 больше похож на ту же проблему безопасности исключений для конкретной платформы, чем на состояние гонки. - person Patrick Parker; 18.04.2019