Home Page
Search
\begin{md} # 进程锁实现 在开发过程中,有些程序需要避免多开以防止多进程同时对一个文件进行操作产生冲突。为了达到目的,可以使用文件锁来实现进程锁。原理很简单,就是让进程在启动时获取某个文件的独占锁,这样子如果再启动这个程序的另一个实例的话,会由于获取不到这个文件的独占锁而导致启动异常,程序员可以检测这个异常情况,在返回这个异常的时候让程序主动退出,以实现只允许程序单开的功能。 下面提供一个进程锁的 Windows/Linux 双平台的实现。 ```c++ // ProcessLock.h #pragma once //#include
#include
#include
#include
#include
#include
#ifdef WIN32 #include
#else #include
#endif #define PROCESS_LOCK_BUF_SIZE 16 #ifndef ERR_EXIT #define ERR_EXIT(msg) \ do { \ printf(msg "\n"); \ exit(1); \ } while(0) #endif class ProcessLock { public: ProcessLock(); bool CreateLock(const char* filename); void FreeLock(); private: #ifdef WIN32 HANDLE handler; #else int fd; #endif }; #ifdef WIN32 ProcessLock::ProcessLock() { handler = INVALID_HANDLE_VALUE; } bool ProcessLock::CreateLock(const char* filename) { bool result = false; // 创建文件 handler = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); if (handler == INVALID_HANDLE_VALUE) goto exit; // 获取文件锁 if (!LockFile(handler, 0, 0, 1, 0)) { printf("CreateLock() failed. there are another process."); goto exit; } result = true; exit: // 如果获取文件锁失败,退出前需要关闭文件 if (!result && handler != INVALID_HANDLE_VALUE) { if (CloseHandle(handler)) printf("CreateLock() failed. CloseHandle() failed\n"); handler = INVALID_HANDLE_VALUE; } return result; } void ProcessLock::FreeLock() { if (handler == INVALID_HANDLE_VALUE) ERR_EXIT("FreeLock() failed. handle == INVALID_HANDLE_VALUE"); // 释放文件锁 if (!UnlockFile(handler, 0, 0, 1, 0)) ERR_EXIT("FreeLock() failed. UnlockFile() failed"); // 关闭文件 CloseHandle(handler); ERR_EXIT("FreeLock() failed. CloseHandle() failed"); handler = INVALID_HANDLE_VALUE; } #else // Linux OS ProcessLock::ProcessLock() { fd = -1; } bool ProcessLock::CreateLock(const char* filename) { bool result = false; struct flock file_lock; char buf[PROCESS_LOCK_BUF_SIZE]; memset(buf, 0, sizeof(buf)); memset(&file_lock, 0, sizeof(file_lock)); // 创建文件 fd = creat(filename, S_IWUSR); if (fd == -1) ERR_EXIT("CreateLock() failed. creat() failed"); file_lock.l_type = F_WRLCK; file_lock.l_whence = SEEK_SET; file_lock.l_start = 0; file_lock.l_len = 0; // 获取文件锁 if (fcntl(fd, F_SETLK, &file_lock)) { if (errno == EACCES || errno == EAGAIN) ERR_EXIT("CreateLock() failed. there are another process"); else ERR_EXIT("CreateLock() failed. fcntl() failed"); goto exit; } // 将文件大小设置为0 if (ftruncate(fd, 0)) { ERR_EXIT("CreateLock() failed. ftruncate() failed"); goto exit; } // 将进程 pid 写入文件 snprintf(buf, PROCESS_LOCK_BUF_SIZE, "%ld\n", (long)getpid()); if (write(fd, buf, strlen(buf)) != strlen(buf)) ERR_EXIT("CreateLock() failed. write() failed"); result = true; exit: if (!result && fd != -1) { close(fd); fd = -1; } return result; } void ProcessLock::FreeLock() { int ret = false; struct flock file_lock; if (fd == -1) { ERR_EXIT("FreeLock() failed. fd == -1"); exit(1); } file_lock.l_type = F_UNLCK; file_lock.l_whence = SEEK_SET; file_lock.l_start = 0; file_lock.l_len = 0; // 释放文件锁 if (fcntl(fd, F_SETLK, &file_lock) == -1) { ERR_EXIT("FreeLock() failed. fcntl() failed"); exit(1); } if (close(fd) == -1) { ERR_EXIT("FreeLock() failed. close() failed"); exit(1); } fd = -1; } #endif // ifdef WIN32 ``` 测试程序 ```c++ // testProcessLock.cpp #include "ProcessLock.h" int main() { ProcessLock pl; bool ret = pl.CreateLock("ProcessLock.lock"); if (!ret) { getc(stdin); exit(1); } else { while(1) { printf("tick\n"); #ifdef WIN32 Sleep(1000); #else sleep(1); #endif } } } ``` \end{md}
Home Page