std::istream::getline是否线程安全

试验

将若干文件的路径写进入一个文本文档中,然后读取文档中保存的文件路径,然后计算MD5值并写入MySQL数据库

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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class FileMD5Thread {
private:
char* listFile;
char* outMD5File;
unsigned short int threadCount = 24;//线程数
static std::mutex logLock;//日志锁
std::mutex* plck;//默认所有对象使用相同的锁

......
}

int FileMD5Thread::fileMd5Sum(sql::Statement* state, ThreadsAction action)
{
constexpr int FILE_NAME_PATH_SIZE = 512;
//char* buff = new char[FILE_NAME_PATH_SIZE*2];
char buff[FILE_NAME_PATH_SIZE * 2] = { 0 };
char buffMD5[128] = { 0 };
char fileName[FILE_NAME_PATH_SIZE] = { 0 };
char filePath[FILE_NAME_PATH_SIZE] = { 0 };
while (inFile.getline(buff, FILE_NAME_PATH_SIZE))
{
try
{// 捕捉getFileMD5抛出的错误
strcpy(buffMD5, getFileMD5(buff).c_str());
}
catch (...)
{
this->endResetZero(buffMD5, 128);
std::lock_guard<std::mutex> lockguard(logLock);
errors++;
outLog << "无法定位文件: " << buff << endl;
cout << "无法定位文件: " << buff << endl;
}
cutStr(buff, filePath, fileName, '\\');
switch (action)
{
case FileMD5Thread::ThreadsAction::TO_MYSQL:
{ /*写入数据库*/
sprintf(buff, "insert into mods_test(md5,filename,path) value(\"%s\",\"%s\",\"%s\")", buffMD5, fileName, filePath);
gbkToUTF8(buff, FILE_NAME_PATH_SIZE);
try
{
std::lock_guard<std::mutex> lockguard(*plck);
state->executeUpdate(buff);
}
catch (...)
{
std::lock_guard<std::mutex> lockguard(logLock);
errors++;
outLog << buff << endl;
}
}
break;
case FileMD5Thread::ThreadsAction::TO_FILE:
{ /*写入文件*/
std::lock_guard<std::mutex> lockguard(*plck);
//outFile << buff << endl << "[MD5]" << buffMD5 << endl;//写入文件
outLog << fileName << " " << filePath << endl;
outLog << buff << endl << endl;
}
break;
default:
{
cout << "别乱搞!" << endl;
return 404;
}
break;
}//switch end
this->endResetZero(buff, FILE_NAME_PATH_SIZE);
this->endResetZero(fileName, FILE_NAME_PATH_SIZE);
this->endResetZero(filePath, FILE_NAME_PATH_SIZE);
}
//delete[] buff;
return errors;
}
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
/*==========以下是开启多线程的函数==========*/
int FileMD5Thread::run(sql::Connection* conn)
{
/*传入Connection对象时,写入MySQL数据库
*/
clock_t start_time = clock();//计时开始

openIoFile();
std::mutex lckSQL;//SQL写入锁
this->plck = &lckSQL;
sql::Statement* state = conn->createStatement();
thread* pth = new thread[threadCount];
if (pth == NULL) {
return 404;
}
int i;
for (i = 0; i < threadCount; i++) {
pth[i] = thread(&FileMD5Thread::fileMd5Sum, this, state, ThreadsAction::TO_MYSQL);
}
for (i = 0; i < threadCount; i++) {
pth[i].join();
}
//计时结束
clock_t end_time = clock();
outLog << "elapsed time:" << (double)clock() / CLK_TCK << "s\n"
<< "error count: " << errors;
//收尾
state->close();
delete state;
delete[] pth;
closeIoFile();
return errors;
}

在MySQL查询结果

1
2
mysql> SELECT md5,COUNT(*) AS CONTMD5 FROM mods_test GROUP BY md5 HAVING CONTMD5%14!=0;
Empty set

我一共运行了14次程序.

此时对数据进行分组并计数,只要计数能被14整除,那说明getline在多线程操读取且没有上锁的情况下,没有重复读取一行或者漏读一行数据.

从试验结果来看,getline是线程安全的.