前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >日更系列 - 又一次碰到非线程安全std容器的core

日更系列 - 又一次碰到非线程安全std容器的core

原创
作者头像
mariolu
发布2022-06-02 17:52:10
8970
发布2022-06-02 17:52:10
举报

众所周知,std容器是非线程安全的,跟非线程安全的容器,如果代码core掉,通常会在容器的一些方法函数中。因为这类的core文件往往显示不是很直观,很多c++ std新手往往对这类型core无从下手。所以这里做一些纪录以总结通用经验给后人享用。

一、业务背景

一个专业背景推荐知识是:这里设计了一个数据结构m_cvr2是进行cvr打分。

std::unordered_map<int32_t,std::unordered_map<int64_t,double> > m_cvr2;

分别对应着<场景id,<应用广告id,打分>>。分为一些场景,然后每个场景都以单独线程发给上游预估服务打分。线程间是并行计算过程。最后汇聚所有场景的打分信息。

二、问题复现

有一天,开发代码进行了灰度发布,隔一段时间会有个core文件。使用gdb打印了信息如下。

把m_cvr2的内容进行了打印(因为容器元素很多,这里使用了gdb内置命令set logging on,将std out屏幕输出写份副本到文件名gdb.txt。作为对比,我们也打印了m_ctr和m_cvr容器)

可以看到这个m_cvr2的[场景id为258]的map没有clear成功。

对比m_ctr或者m_cvr的[场景id为258]的map已经成功clear了。

这次出现的core文件出现在unordered_map::clear()。对m_cvr2[theme_id]的unordered_map操作出了异常。

也就是这里m_cvr2的数据结构是这样定义:

std::unordered_map<int32_t, std::unordered_map<int64_t, double>> m_cvr2;

然后对m_cvr2[theme_id]剥离得到了一个结构体为std::unordered_map<int64_t, double>的成员。这个成员本身也是一个unordered map,它也不是线程安全的。这里有个背景要说明的是,因为我们通过theme_id做了线程的区分。比如说有theme_id从1...30,那么有对应的30个m_cvr2[theme_id],所以可以保证每个线程只限制访问到自己的m_cvr2[theme_id]。

所以其实这个clear操作是没有问题的。

三、深究原因

那么是什么导致的。首先我们需要了解到std容器operator[index]背后的机制。他首先会去根据index索引find是否存在这个元素,如果没有,会进行new一个新成员,并insert到容器中,之后就操作这个元素。如果有find这个元素,那么就直接操作这个元素了。

所以这里有多少个them,就有多少个线程并行竞争访问操作这个m_cvr2。如果是只读这个m_cvr2本身是没问题的,但是一旦有线程没有find去创建新元素,那就会导致其他对m_cvr2的操作有几率出现core文件。

所以避免问题的一个办法是,对于并发线程来说,我们只要保证他们是只读访问就行了。那么只读访问,我们可以在初始化统一有一个线程去把所有元素都创建出来。那么后续线程并发访问使用operator[index]操作都有现成的元素可以使用。这里类似m_ctr和m_cvr,把元素都初始化预填充出来。

四、总结

这类问题起因不是直观的,因为不是我们stl中容器的clear实现有bug,本质上我们没有很熟悉容器是非线程安全的特性。所以总结一些使用std容器的一些准则,

  • 并发使用不新增元素(这里元素如果是简单类型pld是允许的,而非pld类型,如string或者各种类结构体是不允许的)
  • 使用前在同一线程进行预填充。
  • operator[]操作背后会存在find不到元素进行新增容器元素的操作。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、业务背景
  • 二、问题复现
  • 三、深究原因
  • 四、总结
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档