多线程问题之一:修改全局变量

一、在多线程同时调用同一个函数,而该函数修改全局变量。

问题:我传了字符串进入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.碰到问题一定要思考把背后的原因机制分析清楚,越积累越多,研究也就越广越深。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值