TA的每日心情 | 慵懒 昨天 10:54 |
---|
签到天数: 186 天 连续签到: 2 天 [LV.7]常住居民III
|
本帖最后由 stm1024 于 2024-12-2 22:09 编辑
单实例程序是一种设计模式,用于确保一个应用程序或服务的只有一个实例在运行。常见于桌面应用程序中,尤其是在需要确保不重复打开多个窗口或会话的情况下,例如有独占硬件资源的程序,再打开一个程序已经毫无意义,只会浪费系统资源和报错;或者即使没有独占的程序,甚至还会扰乱正在执行的程序。
1. 原理
以下是在RK3568上实现单实例程序的基本思路:
利用共享内存,开辟一块区域给程序使用。
当程序运行时,检查共享内存是否存在:
如果不存在则表明之前程序没有运行过,或者已经正常退出,此时初始化共享内存区域,并向其中写入当前时间戳;
如果存在,检查该区域内的数据与当前时间戳的差值,如果小于给定阈值,则表明已有实例在运行,当前程序退出;
如果差值大于给定阈值,则认为是之前同样实例的程序非正常退出,此时更新其中数据为当前时间戳;
程序在运行时,定时写入当前时间戳到共享内存中;
程序退出时,回收共享内存。
2. 实现
使用QT的共享内存方案,主要代码如下:
- #include "widget.h"
- #include "ui_widget.h"
- Widget::Widget(QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::Widget)
- {
- ui->setupUi(this);
- tim=new QTimer();
- connect(tim,SIGNAL(timeout()),this,SLOT(updateData()));
- shm=new QSharedMemory(this);
- shm->setKey(QString("XD_SINGLE_INSTANCE"));
- //if can attach,
- if(shm->attach())
- {
- char dat[SHM_SIZE];
- memset(dat,0x00,SHM_SIZE);
- //retrive previous timestamp
- shm->lock();
- memmove(dat,shm->constData(),SHM_SIZE);
- shm->unlock();
- qint64* pre_ts=(qint64*)(dat);
- //then check timestamp
- if(QDateTime::currentMSecsSinceEpoch()-(*pre_ts)<2*INTERVAL)//epoch is very near
- {
- //so current instance should quit
- qDebug()<<"previous instance running normally, i quit";
- exit(1);
- }
- else//epoch diff too much
- {
- //so current instance should run & update
- qDebug()<<"previous instance crashed,i run";
- updateData();
- tim->start(INTERVAL);
- }
- }
- else
- {
- /*
- * if can not attach due to not found key, that means:
- * 1. this instance has never run before;
- * 2. this instance has quit normally;
- * either way, create & attach
- */
- if(shm->error()==QSharedMemory::NotFound)
- {
- qDebug()<<"no instance is running, i run";
- shm->create(SHM_SIZE);
- updateData();
- tim->start(INTERVAL);
- }
- }
- }
- Widget::~Widget()
- {
- if(shm!=nullptr)
- {
- shm->detach();
- delete shm;
- shm=nullptr;
- }
- delete ui;
- }
- void Widget::updateData()
- {
- qint64 ts=QDateTime::currentMSecsSinceEpoch();
- //write current timestamp to shared memory
- if(shm->isAttached())
- {
- shm->lock();
- memmove(shm->data(),(void *)(&ts),SHM_SIZE);
- shm->unlock();
- qDebug().noquote()<<"update shared memory with Timestamp ="<<ts;
- }
- }
复制代码 3. 编译运行与测试
自然还是熟悉的qmake和make:
在Debian环境下运行:
之前没有实例运行,程序正常运行
已有实例运行,退出
另一个实例非正常退出后,启动新实例
可以看到,在这几种情况下实例程序都得到了正确的识别与运行。当然,这个示例主要是用于阐述程序的原理,窗口程序并未有什么实际的作用,此外,向终端中打印时间戳也不是必须的,主要是为了阐述原理。
|
|