stm1024 发表于 2024-12-2 22:07:23

【米尔-MYD-LR3568-GK开发板--试用评测】20——单实例程序

本帖最后由 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;
      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环境下运行:
之前没有实例运行,程序正常运行
已有实例运行,退出

另一个实例非正常退出后,启动新实例
可以看到,在这几种情况下实例程序都得到了正确的识别与运行。当然,这个示例主要是用于阐述程序的原理,窗口程序并未有什么实际的作用,此外,向终端中打印时间戳也不是必须的,主要是为了阐述原理。

页: [1]
查看完整版本: 【米尔-MYD-LR3568-GK开发板--试用评测】20——单实例程序