Lock files可以用于进程间进行通信。比如,我们想要让某些资源被多个进程共享,有一个程序用于发布更新的资源,而其他一些程序可以使用最新的资源做一些事情。由于reader程序可能是一些batch file,所以不太可能使用一些比较高级的同步工具来锁定我们需要发布的资源。这样使用lock files将会是比较直接、简单的方法。
我们需要用lock files来实现简单的Reader-Writer锁,也就是当有人需要进行写操作时,所有其他操作都是不允许的。但是当有人进行读操作时,只有读操作时允许的,写操作依然是被禁止的。
#include <windows.h> #include <cstdlib> #include <cstring> #include <iostream> #include <tchar.h> enum lock_mode { lm_shareread, lm_exclusive, lm_count }; int main(int argc, char** argv) { if (argc >= 2) { lock_mode m; DWORD dwAccess; DWORD dwShareMode; if (strcmp(argv[1], "-r") == 0) { dwAccess = GENERIC_READ; dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE; // share read m = lm_shareread; } else if (strcmp(argv[1], "-w") == 0) { dwAccess = GENERIC_WRITE; dwShareMode = FILE_SHARE_DELETE; // do not share read or write, exclusive m = lm_exclusive; } else { std::cerr << "invalid argument/n"; return EXIT_FAILURE; } HANDLE hFile; hFile = CreateFile(_T("lock"), dwAccess, dwShareMode, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (hFile != INVALID_HANDLE_VALUE) { #ifdef _DEBUG char c; std::cin >> c; #endif if (m == lm_shareread) { std::cout << "read complete/n"; } else { std::cout << "write complete/n"; } CloseHandle(hFile); return EXIT_SUCCESS; } else { if (m == lm_shareread) { std::cerr << "Cannot read/n"; } else { std::cerr << "Cannot write/n"; } std::cerr << "Error code: " << GetLastError() << std::endl; return EXIT_FAILURE; } } else { std::cerr << "no arguments specified/n"; return EXIT_FAILURE; } }
这个程序接受2种参数,-r, -w,-r进行读,-w进行写,可以试着模拟两种file locks的情况。
这里我们使用FILE_FLAG_DELETE_ON_CLOSE,由于file locks通常是临时文件,所以当最后一个文件句柄关闭的时候,将会自动删除这个文件,而FILE_ATTRIBUTE_TEMPORARY是说,这是个临时文件,对它的写入将不用写回到磁盘上(不加也没有关系,因为我们这里不会有任何存取操作)。
在创建文件时,我们使用了OPEN_ALWAYS。因为如果不存在,我们肯定需要创建一个文件,但是如果已经存在,那么我们希望直接打开这个文件。 如果将它们分开为2个操作: 1. 测试文件是否存在 2. 打开或者创建文件
将会有race condition出现。假设我们测试完后发现文件不存在,那么我们决定创建文件,但是此时另外一个程序也发现文件不存在,也准备创建文件,那么其中一个创建操作必然会失败,这里我们想要的是atomic operation,而OPEN_ALWAYS正好保证了这种情况。