C++,保证多进程中同一时刻只有一个实例
·
代码:
#include <atomic>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <semaphore.h>
#include <cstring>
#include <cerrno>
#include <signal.h>
#include <cstdio>
const char* SHM_NAME = "/myclass_test6_instance";
const char* SEM_NAME = "/myclass_test6_sem";
struct SharedData {
bool active;
pid_t owner_pid;
uint64_t generation;
};
class ScopedSemLock {
public:
explicit ScopedSemLock(sem_t* sem)
: mSem(sem), mLocked(false), mErrno(0) {
if (!mSem) {
mErrno = EINVAL;
return;
}
while (true) {
if (sem_wait(mSem) == 0) {
mLocked = true;
return;
}
if (errno == EINTR) {
continue;
}
mErrno = errno;
break;
}
}
~ScopedSemLock() {
if (mLocked && sem_post(mSem) == -1) {
std::perror("sem_post");
}
}
bool locked() const { return mLocked; }
int error() const { return mErrno; }
private:
sem_t* mSem;
bool mLocked;
int mErrno;
};
class SPMyClass;
class MyClass;
class SPMyClass {
public:
SPMyClass() = default;
virtual ~SPMyClass() = default;
static SPMyClass* create();
static void destroy(void* instance);
};
class MyClass : public SPMyClass {
public:
static std::atomic<bool> mInstanceExists;
MyClass();
~MyClass() override;
void create();
void destroy();
bool isCreated() const { return mIsCreated; }
private:
bool mIsCreated;
bool mDestroyed;
static bool initSharedResources();
static void releaseSharedResources();
static bool isProcessAlive(pid_t pid);
static SharedData* mSharedData;
static sem_t* mSemaphore;
static int mShmFd;
static bool mIsInitialized;
static std::atomic<int> mObjectCounter;
};
std::atomic<bool> MyClass::mInstanceExists{false};
std::atomic<int> MyClass::mObjectCounter{0};
SharedData* MyClass::mSharedData = nullptr;
sem_t* MyClass::mSemaphore = nullptr;
int MyClass::mShmFd = -1;
bool MyClass::mIsInitialized = false;
MyClass::MyClass()
: mIsCreated(false), mDestroyed(false) {
mObjectCounter.fetch_add(1, std::memory_order_relaxed);
}
MyClass::~MyClass() {
destroy();
if (mObjectCounter.fetch_sub(1, std::memory_order_acq_rel) == 1) {
releaseSharedResources();
}
}
bool MyClass::isProcessAlive(pid_t pid) {
if (pid <= 0) {
return false;
}
if (::kill(pid, 0) == 0) {
return true;
}
return errno != ESRCH;
}
bool MyClass::initSharedResources() {
if (mIsInitialized) {
return true;
}
sem_t* semaphore = sem_open(SEM_NAME, O_CREAT, 0666, 1);
if (semaphore == SEM_FAILED) {
std::perror("sem_open");
return false;
}
int shmFd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0666);
if (shmFd == -1) {
std::perror("shm_open");
sem_close(semaphore);
return false;
}
struct stat st {};
if (fstat(shmFd, &st) == -1) {
std::perror("fstat");
::close(shmFd);
sem_close(semaphore);
return false;
}
bool needInit = st.st_size < static_cast<off_t>(sizeof(SharedData));
if (needInit) {
if (ftruncate(shmFd, sizeof(SharedData)) == -1) {
std::perror("ftruncate");
::close(shmFd);
sem_close(semaphore);
return false;
}
}
void* addr = mmap(nullptr, sizeof(SharedData),
PROT_READ | PROT_WRITE,
MAP_SHARED,
shmFd, 0);
if (addr == MAP_FAILED) {
std::perror("mmap");
::close(shmFd);
sem_close(semaphore);
return false;
}
mSemaphore = semaphore;
mShmFd = shmFd;
mSharedData = static_cast<SharedData*>(addr);
if (needInit) {
std::memset(mSharedData, 0, sizeof(SharedData));
}
mIsInitialized = true;
return true;
}
void MyClass::releaseSharedResources() {
if (mSharedData) {
munmap(mSharedData, sizeof(SharedData));
mSharedData = nullptr;
}
if (mShmFd != -1) {
::close(mShmFd);
mShmFd = -1;
}
if (mSemaphore) {
sem_close(mSemaphore);
mSemaphore = nullptr;
}
mIsInitialized = false;
}
void MyClass::create() {
if (mIsCreated) {
std::cout << "当前对象已经拥有实例,无需重复创建。" << std::endl;
return;
}
if (!initSharedResources()) {
std::cerr << "初始化共享资源失败,无法创建实例。" << std::endl;
return;
}
ScopedSemLock lock(mSemaphore);
if (!lock.locked()) {
std::cerr << "获取信号量失败: "
<< std::strerror(lock.error()) << std::endl;
return;
}
const pid_t myPid = ::getpid();
if (!mSharedData->active) {
mSharedData->active = true;
mSharedData->owner_pid = myPid;
++mSharedData->generation;
mIsCreated = true;
mInstanceExists.store(true, std::memory_order_release);
std::cout << "成功创建实例,PID: " << myPid << std::endl;
return;
}
const pid_t ownerPid = mSharedData->owner_pid;
if (!isProcessAlive(ownerPid)) {
std::cout << "检测到僵尸实例 (PID " << ownerPid
<< "),接管实例。" << std::endl;
mSharedData->active = true;
mSharedData->owner_pid = myPid;
++mSharedData->generation;
mIsCreated = true;
mInstanceExists.store(true, std::memory_order_release);
std::cout << "成功创建实例,PID: " << myPid << std::endl;
return;
}
if (ownerPid == myPid) {
std::cout << "当前进程已经持有全局实例,禁止重复创建。" << std::endl;
} else {
std::cout << "已有其他进程在运行,持有者 PID: "
<< ownerPid << std::endl;
}
}
void MyClass::destroy() {
if (mDestroyed) {
return;
}
mDestroyed = true;
if (mIsCreated && mSemaphore && mSharedData) {
ScopedSemLock lock(mSemaphore);
if (!lock.locked()) {
std::cerr << "destroy: 获取信号量失败: "
<< std::strerror(lock.error()) << std::endl;
} else if (mSharedData->active &&
mSharedData->owner_pid == ::getpid()) {
mSharedData->active = false;
mSharedData->owner_pid = 0;
++mSharedData->generation;
mInstanceExists.store(false, std::memory_order_release);
std::cout << "实例已销毁,PID: " << ::getpid() << std::endl;
}
}
mIsCreated = false;
}
SPMyClass* SPMyClass::create() {
MyClass* instance = new MyClass();
instance->create();
if (!instance->isCreated()) {
delete instance;
return nullptr;
}
return instance;
}
void SPMyClass::destroy(void* instance) {
if (!instance) {
return;
}
static_cast<MyClass*>(instance)->destroy();
}
int main() {
std::cout << "Before instance: "
<< MyClass::mInstanceExists.load() << std::endl;
SPMyClass* a = SPMyClass::create();
std::cout << "After instance a: "
<< MyClass::mInstanceExists.load() << std::endl;
SPMyClass* b = SPMyClass::create();
std::cout << "After instance b: "
<< MyClass::mInstanceExists.load() << std::endl;
std::cout << "等待100秒..." << std::endl;
sleep(100);
std::cout << "\n删除实例a..." << std::endl;
delete a;
std::cout << "After delete a: "
<< MyClass::mInstanceExists.load() << std::endl;
std::cout << "\n删除实例b..." << std::endl;
delete b;
std::cout << "After delete b: "
<< MyClass::mInstanceExists.load() << std::endl;
return 0;
}
主要原理解析
1. 共享内存 + 信号量
-
共享内存(
shm_open+mmap)
通过 POSIX 共享内存,所有进程都映射到同一块内存区域SharedData,用来存储实例的状态信息。 -
信号量(
sem_open)
用信号量做进程间同步,保证对共享内存的访问是互斥的,避免竞态条件。
2. 共享数据结构 SharedData
struct SharedData {
bool active; // 实例是否被占用
pid_t owner_pid; // 拥有实例的进程ID
uint64_t generation; // 版本号,表示实例状态的变化次数
};
active标记实例是否被占用。owner_pid记录当前拥有实例的进程ID。generation用于版本控制(可用于检测实例是否被重置等)。
3. 创建实例的流程(MyClass::create())
- 先初始化共享资源(共享内存和信号量)。
- 通过信号量加锁,保证对共享内存的独占访问。
- 检查
SharedData中的active标志:- 如果
false,说明没有进程持有实例,当前进程设置active = true,owner_pid = 当前进程PID,成功创建实例。 - 如果
true,说明已有进程持有实例,进一步检查持有者进程是否存活:- 如果持有者进程已死(僵尸进程),当前进程“接管”实例,更新
owner_pid和generation。 - 如果持有者进程仍然存活:
- 如果是当前进程自己,拒绝重复创建。
- 如果是其他进程,拒绝创建,提示已有其他进程持有实例。
- 如果持有者进程已死(僵尸进程),当前进程“接管”实例,更新
- 如果
4. 销毁实例的流程(MyClass::destroy())
- 通过信号量加锁,保证对共享内存的独占访问。
- 如果当前进程是实例拥有者,清除
active标志和owner_pid,表示实例释放。 - 更新
generation,通知状态变化。 - 其他进程检测到
active == false后,可以创建新实例。
5. 进程存活检测(isProcessAlive(pid_t pid))
- 通过
kill(pid, 0)系统调用检测进程是否存在。 - 如果进程不存在,允许其他进程接管实例。
如何保证多进程中只有一个真实实例?
- 共享内存:所有进程共享同一份状态数据。
- 信号量:保证对共享状态的访问是互斥的,避免竞态。
- 进程存活检测:避免死锁或僵尸实例占用资源。
- 原子变量
mInstanceExists:本地进程内标记实例是否存在,方便快速判断。
总结
- 通过共享内存和信号量实现跨进程的“单例”状态管理。
- 只有一个进程能成功将
SharedData::active置为true并成为拥有者。 - 其他进程要么检测到实例已被占用且持有者存活,拒绝创建;要么检测到持有者死掉,接管实例。
- 进程退出时释放实例,允许其他进程创建。
更多推荐



所有评论(0)