在 Qt 中实现应用单例运行(确保同一时间只有一个实例),可以通过以下几种方法实现:
方法 1:使用共享内存(QSharedMemory) - 跨平台
cpp
复制
下载
#include <QSharedMemory>
#include <QMessageBox>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 创建唯一标识的共享内存
QSharedMemory shared("MyAppSingleInstanceKey");
if (!shared.create(1)) { // 尝试创建 1 字节内存
if (shared.error() == QSharedMemory::AlreadyExists) {
QMessageBox::warning(nullptr, "警告", "程序已在运行中!");
return 0;
}
// 其他错误处理
return 1;
}
// 主程序逻辑
MainWindow w;
w.show();
// 程序退出时自动释放共享内存
return a.exec();
}
方法 2:使用本地套接字(QLocalServer) - 更可靠(推荐)
cpp
复制
下载
#include <QLocalServer>
#include <QLocalSocket>
#include <QMessageBox>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
const QString serverName = "MyAppSingleInstanceServer";
QLocalSocket socket;
socket.connectToServer(serverName);
// 检查是否已有实例运行
if (socket.waitForConnected(500)) {
QMessageBox::warning(nullptr, "警告", "程序已在运行中!");
// 可选:发送消息激活已有实例
socket.write("ACTIVATE");
socket.flush();
socket.close();
return 0;
}
// 创建本地服务器
QLocalServer server;
if (!server.listen(serverName)) {
QMessageBox::critical(nullptr, "错误", "无法创建单例锁!");
return 1;
}
// 主程序逻辑
MainWindow w;
QObject::connect(&server, &QLocalServer::newConnection, [&]() {
QLocalSocket *client = server.nextPendingConnection();
// 收到激活请求时唤醒窗口
w.raise();
w.activateWindow();
client->deleteLater();
});
w.show();
return a.exec();
}
方法 3:使用文件锁(QFile + QLockFile) - 简单轻量
cpp
复制
下载
#include <QLockFile>
#include <QDir>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
// 创建锁文件(通常放在临时目录)
QLockFile lockFile(QDir::temp().absoluteFilePath("MyApp.lock"));
if (!lockFile.tryLock(100)) {
QMessageBox::warning(nullptr, "警告", "程序已在运行中!");
return 0;
}
// 主程序逻辑
MainWindow w;
w.show();
return a.exec();
// 程序退出时自动释放锁
}
方案对比
方法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
共享内存 | 简单直接 | 程序崩溃时可能残留锁 | 简单应用 |
本地套接字 | 支持进程间通信,可靠性高 | 代码稍复杂 | 需要激活已有实例的场景 |
文件锁 | 跨平台,自动清理 | 依赖文件系统权限 | 所有平台通用方案 |
最佳实践建议
-
推荐组合使用:本地套接字 + 文件锁
-
用
QLockFile
确保基础单例 -
用
QLocalServer
实现激活已有实例功能
-
-
唯一标识注意事项:
-
使用应用专属 ID(如
com.company.appname
) -
避免使用普通用户无权限的路径
-
-
崩溃处理:
-
共享内存方案需处理
QSharedMemory::AlreadyExists
错误 -
文件锁方案可设置
lockFile.setStaleLockTime(0)
禁用旧锁检测
-
完整示例(本地套接字方案增强版)
cpp
复制
下载
#include <QLocalServer>
#include <QLocalSocket>
#include <QMessageBox>
#include <QLockFile>
#include <QDir>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 双重检查:文件锁 + 套接字
QLockFile lockFile(QDir::temp().filePath("MyApp.lock"));
if (!lockFile.tryLock(100)) {
// 尝试唤醒已有实例
QLocalSocket socket;
socket.connectToServer("MyAppServer");
if (socket.waitForConnected(500)) {
socket.write("ACTIVATE");
socket.waitForBytesWritten();
}
return 0;
}
// 创建本地服务器
QLocalServer server;
if (!server.listen("MyAppServer")) {
QMessageBox::critical(nullptr, "错误", "无法创建进程锁");
return 1;
}
MainWindow w;
// 处理激活请求
QObject::connect(&server, &QLocalServer::newConnection, [&]() {
if (QLocalSocket *socket = server.nextPendingConnection()) {
socket->waitForReadyRead(100);
w.showNormal();
w.raise();
w.activateWindow();
socket->deleteLater();
}
});
// 正常启动
w.show();
return app.exec();
}
重要提示 :在程序退出时,
QLockFile
和QLocalServer
会自动释放资源。对于共享内存方案,需确保程序正常退出(或添加崩溃处理