回答

收藏

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

#板卡评测 #板卡评测 1577 人阅读 | 0 人回复 | 2024-12-02

本帖最后由 stm1024 于 2024-12-2 22:09 编辑

单实例程序是一种设计模式,用于确保一个应用程序或服务的只有一个实例在运行。常见于桌面应用程序中,尤其是在需要确保不重复打开多个窗口或会话的情况下,例如有独占硬件资源的程序,再打开一个程序已经毫无意义,只会浪费系统资源和报错;或者即使没有独占的程序,甚至还会扰乱正在执行的程序。
1. 原理
以下是在RK3568上实现单实例程序的基本思路:

利用共享内存,开辟一块区域给程序使用。
当程序运行时,检查共享内存是否存在:
    如果不存在则表明之前程序没有运行过,或者已经正常退出,此时初始化共享内存区域,并向其中写入当前时间戳;
    如果存在,检查该区域内的数据与当前时间戳的差值,如果小于给定阈值,则表明已有实例在运行,当前程序退出;
           如果差值大于给定阈值,则认为是之前同样实例的程序非正常退出,此时更新其中数据为当前时间戳;
    程序在运行时,定时写入当前时间戳到共享内存中;
    程序退出时,回收共享内存。

2. 实现
使用QT的共享内存方案,主要代码如下:
  1. #include "widget.h"
  2. #include "ui_widget.h"

  3. Widget::Widget(QWidget *parent)
  4.     : QWidget(parent)
  5.     , ui(new Ui::Widget)
  6. {
  7.     ui->setupUi(this);
  8.     tim=new QTimer();
  9.     connect(tim,SIGNAL(timeout()),this,SLOT(updateData()));

  10.     shm=new QSharedMemory(this);
  11.     shm->setKey(QString("XD_SINGLE_INSTANCE"));
  12.     //if can attach,
  13.     if(shm->attach())
  14.     {
  15.         char dat[SHM_SIZE];
  16.         memset(dat,0x00,SHM_SIZE);
  17.         //retrive previous timestamp
  18.         shm->lock();
  19.         memmove(dat,shm->constData(),SHM_SIZE);
  20.         shm->unlock();
  21.         qint64* pre_ts=(qint64*)(dat);
  22.         //then check timestamp
  23.         if(QDateTime::currentMSecsSinceEpoch()-(*pre_ts)<2*INTERVAL)//epoch is very near
  24.         {
  25.             //so current instance should quit
  26.             qDebug()<<"previous instance running normally, i quit";
  27.             exit(1);
  28.         }
  29.         else//epoch diff too much
  30.         {
  31.             //so current instance should run & update
  32.             qDebug()<<"previous instance crashed,i run";
  33.             updateData();
  34.             tim->start(INTERVAL);
  35.         }
  36.     }
  37.     else
  38.     {
  39.         /*
  40.          * if can not attach due to not found key, that means:
  41.          * 1. this instance has never run before;
  42.          * 2. this instance has quit normally;
  43.          * either way, create & attach
  44.         */
  45.         if(shm->error()==QSharedMemory::NotFound)
  46.         {
  47.             qDebug()<<"no instance is running, i run";
  48.             shm->create(SHM_SIZE);
  49.             updateData();
  50.             tim->start(INTERVAL);
  51.         }
  52.     }
  53. }

  54. Widget::~Widget()
  55. {
  56.     if(shm!=nullptr)
  57.     {
  58.         shm->detach();
  59.         delete shm;
  60.         shm=nullptr;
  61.     }
  62.     delete ui;
  63. }

  64. void Widget::updateData()
  65. {
  66.     qint64 ts=QDateTime::currentMSecsSinceEpoch();
  67.     //write current timestamp to shared memory
  68.     if(shm->isAttached())
  69.     {
  70.         shm->lock();
  71.         memmove(shm->data(),(void *)(&ts),SHM_SIZE);
  72.         shm->unlock();
  73.         qDebug().noquote()<<"update shared memory with Timestamp ="<<ts;
  74.     }
  75. }
复制代码
3. 编译运行与测试
自然还是熟悉的qmake和make:


在Debian环境下运行:
之前没有实例运行,程序正常运行

已有实例运行,退出


另一个实例非正常退出后,启动新实例

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

分享到:
回复

使用道具 举报

您需要登录后才可以回帖 注册/登录

本版积分规则

5300 积分
55 主题
+ 关注
热门推荐
关闭

站长推荐上一条 /3 下一条