INI是Windows系统下人们喜闻乐见的一种配置存储方式。Windows提供了一套简单的接口操作INI文件,但它们并不是线程安全的,对于这一点,这些函数比如WritePrivateProfileString的文档中并没有提到。
据这篇文章介绍:
WritePrivateProfileString:
- WritePrivateProfileString内部使用NtCreateFile访问文件,共享方式设置为:FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE。使用NtLockFile,FailImmediately设置为False,ExlusiveLock设置为True来锁定文件。
- 这意味着WritePrivateProfileString是非线程安全的,是进程安全的(非远程机器)。
我们目前的软件有大量的并发操作INI的行为,没出现过什么问题只能说是幸运了(亦或是不幸)。同事并不太相信以上结论,让我们用事实证明,写一段并发操作INI的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
#include "stdafx.h"
#include <Windows.h>
#include <vector>
#include <string>
#include <process.h>
#include <cassert>
std::wstring GetIniName()
{
std::wstring strIniName;
strIniName.resize(MAX_PATH);
strIniName.resize(GetModuleFileName(NULL, const_cast<TCHAR*>(strIniName.data()), strIniName.size()));
strIniName += _T(".ini");
return strIniName;
}
void WriteIniInThread(void* pText)
{
const TCHAR *pSec = (TCHAR*)pText;
assert(pSec);
std::wstring strVal;
std::wstring strIni = GetIniName();
for (int i=0; i<1000; ++i)
{
strVal = std::to_wstring(_Longlong(i));
WritePrivateProfileString(pSec, strVal.data(), strVal.data(), strIni.data());
}
}
int _tmain(int argc, _TCHAR* argv[])
{
std::vector<std::wstring> threadTexts;
std::vector<HANDLE> threadHandles;
for (int i = 0; i<60; ++i)
{
std::wstring strText = _T("Thread");
strText += std::to_wstring(_Longlong(i));
threadTexts.push_back(strText);
threadHandles.push_back(HANDLE(_beginthread(WriteIniInThread, 0, (void*)threadTexts[i].data())));
}
WaitForMultipleObjects(threadHandles.size(), threadHandles.data(), TRUE, INFINITE);
return 0;
}
|
代码很简单,开60个线程同时往一个INI文件里写东西,让我们对比一下多线程操作INI和非多线程操作的结果,左侧是不使用多线程操作的结果(只贴出前50行):
结果一目了然。(多线程同时操作INI时,每次的结果可能都不一样的)。