Посмотрите на этот пример:
var app = angular.module('plunker', []);
app.controller('MainCtrl', function($scope,$http) {
var getJoke = function(){
return $http.get('http://api.icndb.com/jokes/random').then(function(res){
return res.data.value;
});
}
getJoke().then(function(res) {
console.log(res.joke);
});
});
Как вы можете видеть, getJoke
возвращает разрешенное обещание (оно разрешено при возврате res.data.value
). Таким образом, вы ждете, пока запрос $ http.get не будет завершен, а затем выполнится console.log (res.joke) (как обычный асинхронный поток).
Это plnkr:
для linux:
// ----------------------------------
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);
для Windows:
HANDLE g_app_mutex = NULL;
bool check_one_app_instance () {g_app_mutex = :: CreateMutex (NULL, FALSE, L "8BD290769B404A7816985M9E505CF9AD64"); // это любой другой ключ как строка if (GetLastError () == ERROR_ALREADY_EXISTS) {CloseHandle (g_app_mutex); return false; }
return true;
}
Простое решение, которое делает то, что вы хотите. Без зависимости от сети (как 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();
}
Поскольку QtSingleApplication
относительно устарел и больше не поддерживается, я написал замену, названную SingleApplication .
Он основан на QSharedMemory
и использует QLocalServer
уведомлять родительский процесс о том, что новый экземпляр является нерестом. Он работает на всех платформах и совместим с Qt 5.
Полный код и документация доступны здесь .
Я использую это решение на данный момент.
Однако у него есть недостаток, что программа может запускаться только один раз пользователем, даже если они одновременно регистрируются из нескольких мест.
singleinstance.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;
}
Вы можете использовать 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
}
Согласно документу Qt, приобретенный QSystemSemaphore
не будет автоматически выпущен, если процесс выйдет из строя, не вызвав его деструктор в Unix-подобных операционных системах. Это может стать причиной тупика в другом процессе, пытающегося получить тот же семафор. Если вы хотите быть на 100% уверенным, что ваша программа правильно справляется с аварийными ситуациями, и если вы не настаиваете на использовании Qt, вы можете использовать другие механизмы блокировки, которые операционные системы автоматически освобождают, когда процесс умирает - например, lockf()
и флаг O_EXLOCK
, переданный в open()
, которые упомянуты в . Как восстановить семафор, когда процесс, который уменьшил его до нуля, сработает? или flock()
. На самом деле создание общей памяти больше не требуется, если используется flock()
. Просто использовать flock()
достаточно для защиты одного экземпляра приложения.
Если восстановление семафора от сбоев в Unix не имеет значения, я думаю, что RunGuard из ответа Дмитрия Сазонова все еще может несколько упрощается:
~RunGuard()
и RunGuard::release()
может быть снят, поскольку QSharedMemory
будет автоматически отсоединяться от сегмента разделяемой памяти после его уничтожения, как в документе Qt для QSharedMemory::~QSharedMemory()
: «Деструктор очищает ключ, который заставляет объект разделяемой памяти отсоединяться от основного сегмента разделяемой памяти». RunGuard::isAnotherRunning()
также может быть снята. Целью является эксклюзивное исполнение. Как отметил @Nejat, мы можем просто воспользоваться тем фактом, что в любой момент может быть создано не более одного сегмента разделяемой памяти для данного ключа, как в документе Qt для QSharedMemory::create()
: «Если сегмент разделяемой памяти, идентифицированный ключ уже существует, операция attach не выполняется и возвращается false. "QSharedMemory
в конструкторе - уничтожить сегмент разделяемой памяти, который выживает из-за предыдущего сбоя процесса, как в документе Qt: «Unix: ... Когда последний поток или процесс, имеющий экземпляр QSharedMemory
, прикрепленный к определенному сегменту разделяемой памяти, отделяется от сегмента, уничтожая его экземпляр QSharedMemory
, ядро Unix выпускает сегмент разделяемой памяти. Но если этот последний поток или процесс выйдет из строя, не запуская деструктор QSharedMemory
, сегмент разделяемой памяти сохранится после сбоя. ". Когда «fix» будет уничтожено, неявный detach()
должен быть вызван его деструктором, и сегмент оставшихся разделяемых разделов, если он есть, будет выпущен. 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
. 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;
}
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)
находится вне защиты блокировки) Другой процесс ProcOther с использованием RunGuard
начинает работать. ProcOther работает до (tag2)
и успешно создает общую память. ProcOther сбой, прежде чем он называет release()
в (tag3)
. ProcCur продолжает работать с (tag1)
. ProcCur работает до (tag2)
и пытается создать общую память. Однако sharedMem.create()
вернет false
, потому что ProcOther оставили созданную. Как мы видим в документе QSharedMemory::create()
: «Если сегмент разделяемой памяти, идентифицированный ключом, уже существует, операция attach не выполняется и возвращается false». Наконец, RunGuard::tryToRun()
в ProcCur вернет false
, что не так ожидаемо, потому что ProcCur является единственным существующим процессом, использующим RunGuard
.