一、在多线程同时调用同一个函数,而该函数修改全局变量。
问题:我传了字符串进入check_path(),但是到了里面就为空""了,也有乱码的情况。
// FIXME: 会有创建失败的情况,发现后我命令行创建是没有问题的,不明原因。
// 后来发现有时候有乱码问题:
// path: “�.” 不存在!
// mkdir -p �.
// path: “�;|�}” 不存在!
// sh: 1: Syntax error: “|” unexpected
// “mkdir -p �;|�}” faild. system result:512
// 有时候有空字符问题:
// path: “” 不存在!
// mkdir: missing operand
// Try ‘mkdir --help’ for more information.
// "mkdir -p " faild. system result:256
代码:
// 判断路径是否存在并且是否有权限
bool check_path(const char* value) {
lock_guard<mutex> lock(mut_cp);
bool ret = true;
if (access(value, F_OK) == -1) {
printf("path: \"%s\" 不存在!\n", value);
char mkdir_command[64];
// FIXME: 会有创建失败的情况,发现后我命令行创建是没有问题的,不明原因。
// 后来发现有时候有乱码问题:
// path: "�." 不存在!
// mkdir -p �.
// path: "�;|�}" 不存在!
// sh: 1: Syntax error: "|" unexpected
// "mkdir -p �;|�}" faild. system result:512
// 有时候有空字符问题:
// path: "" 不存在!
// mkdir: missing operand
// Try 'mkdir --help' for more information.
// "mkdir -p " faild. system result:256
sprintf(mkdir_command, "mkdir -p %s", value);
int result = system(mkdir_command);
// FIXME: 有时候返回是0,但是access检查又不存在,加了延时10ms,也不行。
//std::this_thread::sleep_for(std::chrono::milliseconds(10));
if (result || access(value, F_OK) == -1) {
printf("\"%s\" faild. system result:%d\n", mkdir_command, result);
ret = false;
} else {
printf("mkdir -p %s\n", value);
ret = true;
}
} else {
if (access(value, R_OK) == -1) {
printf("path: \"%s\" 不可读!\n", value);
ret = false;
}
if (access(value, W_OK) == -1) {
printf("path: \"%s\" 不可写!\n", value);
ret = false;
}
}
return ret;
}
// 更新每天保存的日期文件夹
void update_temp_path() {
// 对比当前日期和保存文件夹的日期
char nowdate[10];
memset(nowdate, 0, sizeof(char) * 10);
read_date(nowdate);
const char *ptr = strrchr(EnginePar::temp_path.c_str(), '/') + 1;
// FIXME: mkdir fail sometimes
// if (strcmp(nowdate, ptr) != 0) {
std::string newpath(EnginePar::temp_path.c_str(), int(long(ptr) - long(EnginePar::temp_path.c_str())));
// FIXME: 这里崩过一次,崩的时候查看了这些变量,没发现哪里不对。观察栈,可能与内存释放相关。
EnginePar::temp_path = newpath + std::string(nowdate);
if (!check_path(EnginePar::temp_path.c_str()))
printf("[ERROR] EnginePar::temp_path: %s\n", EnginePar::temp_path.c_str());
// }
}
测试:
因为线程是多线程实时在调用,所以只需要把创建好的日期文件夹删掉,就可以触发该逻辑。
刚开始不断删除文件夹,没有出现这个问题,要多删几次。
分析:
1.check_path()参数进去了就没有了,出来又有了,可以尝试不传指针,传实参。(我没有这样试)
2.check_path()被多线程多次调用,加锁未能解决,因为check_path()加锁了,update_temp_path()还是乱的,所以我将锁修改到update_temp_path()中,便没有再出现该问题了。
3.是多线程的问题,传进去的指针的值被修改了,分析:应该是指针的值被其他线程改了,而被改时把原来的内存释放了。
4.全局变量在多线程情况下不能到处修改,不然线程间会变乱,导致释放问题。
真正原因:
问题出在全局变量EnginePar::temp_path上面,所有的线程都在修改这个全局变量,导致传进去的EnginePar::temp_path值被其他线程修改了。
ps:我将锁修改到update_temp_path()中,也能使之没有再出现问题,也是这个原因。
所以真正的解决办法是不要修改全局变量,使用局部变量去操作。
正常代码:
// 判断路径是否存在并且是否有权限
bool check_path(const char* value) {
bool ret = true;
if (access(value, F_OK) == -1) {
printf("path: \"%s\" 不存在!\n", value);
char mkdir_command[512];
sprintf(mkdir_command, "mkdir -p %s", value);
int result = system(mkdir_command);
if (result || access(value, F_OK) == -1) {
printf("\"%s\" faild. system result:%d\n", mkdir_command, result);
ret = false;
} else {
printf("mkdir -p %s\n", value);
ret = true;
}
} else {
if (access(value, R_OK) == -1) {
printf("path: \"%s\" 不可读!\n", value);
ret = false;
}
if (access(value, W_OK) == -1) {
printf("path: \"%s\" 不可写!\n", value);
ret = false;
}
}
return ret;
}
// 更新每天保存的日期文件夹
std::string update_temp_path() {
std::string tmp_path = EnginePar::temp_path;
if (tmp_path.empty()) {
LOG_WARN("temp_path is empty");
return "";
}
// 对比当前日期和保存文件夹的日期
char nowdate[10];
memset(nowdate, 0, sizeof(char) * 10);
read_date(nowdate);
tmp_path = tmp_path + "/" + nowdate;
//const char *ptr = strrchr(, '/') + 1;
// if (strcmp(nowdate, ptr) != 0) {
//std::string newpath(EnginePar::temp_path.c_str(), int(long(ptr) - long(EnginePar::temp_path.c_str())));
// 尽量不要去修改全局变量,容易被其他线程所修改,而string被修改后地址也会变更,会造成指针指向的区域是已经被回收的区域。
//EnginePar::temp_path = newpath + std::string(nowdate);
if (!check_path(tmp_path.c_str()))
printf("[ERROR] EnginePar::temp_path: %s\n", EnginePar::temp_path.c_str());
// }
return tmp_path;
}
总结
1.多线程情况下尽量不要去修改全局变量,容易被其他线程所修改,而string被修改后地址也会变更,会造成指针指向的区域是已经被回收的区域。
2.最好只有一个地方或线程修改,还要注意读写锁的问题。
3.多线程下全局变量修改要谨慎,再加上修改的还是字符串类型更要注意,数字型的还好一点。
4.碰到问题一定要思考把背后的原因机制分析清楚,越积累越多,研究也就越广越深。