免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12下一页
最近访问板块 发新帖
查看: 13033 | 回复: 11
打印 上一主题 下一主题

[C] 一个全局变量,对其读写是否需要加锁? [复制链接]

论坛徽章:
7
IT运维版块每日发帖之星
日期:2016-05-27 06:20:00IT运维版块每日发帖之星
日期:2016-06-09 06:20:00操作系统版块每日发帖之星
日期:2016-06-12 06:20:00程序设计版块每日发帖之星
日期:2016-06-12 06:20:00操作系统版块每日发帖之星
日期:2016-06-13 06:20:00IT运维版块每日发帖之星
日期:2016-06-17 06:20:002015-2016NBA季后赛纪念章
日期:2016-06-28 17:42:27
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2017-07-28 17:30 |只看该作者 |倒序浏览
大家好,最近遇到一个问题, 我在一个非阻塞的进程中需要动态修改一个变量, 之前一直只有读操作,现在要加入一个写操作,修改这个变量。
我不知道是否需要加锁

请问,是否需要加锁呢?不加锁会产生什么问题呢?

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
2 [报告]
发表于 2017-07-28 19:50 |只看该作者
多个线程操作这个变量需要加,只有一个线程操作就不用了

论坛徽章:
14
水瓶座
日期:2014-06-10 09:51:0215-16赛季CBA联赛之江苏
日期:2017-11-27 11:42:3515-16赛季CBA联赛之八一
日期:2017-04-12 14:26:2815-16赛季CBA联赛之吉林
日期:2016-08-20 10:43:1215-16赛季CBA联赛之广夏
日期:2016-06-23 09:53:58程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-09 06:20:0015-16赛季CBA联赛之上海
日期:2015-12-25 16:40:3515-16赛季CBA联赛之广夏
日期:2015-12-22 09:39:36程序设计版块每日发帖之星
日期:2015-08-24 06:20:002015亚冠之德黑兰石油
日期:2015-08-07 09:57:302015年辞旧岁徽章
日期:2015-03-03 16:54:15
3 [报告]
发表于 2017-07-28 20:03 |只看该作者
  • 如果不是get and set就不用加锁,也就说post value不依赖于pre value。X86下面只要保证可见性就够了
  • 如果post value依赖于pre value,那么可以考虑atomic retry的方式。当然简单加锁也可以。
https://gcc.gnu.org/onlinedocs/gcc-4.5.3/gcc/Atomic-Builtins.html#Atomic-Builtins
  1. volatile int value = 0;

  2. int read_value(void) {
  3.     asm("":::"memory");
  4.     return value;
  5. }

  6. void write_value(int _value) {
  7.     value = _value;
  8.     asm("":::"memory");
  9. }
复制代码

论坛徽章:
0
4 [报告]
发表于 2017-07-29 16:28 |只看该作者
如果同一时刻,会有多个进程/线程,同时对这个变量进行读 和 写的操作,就要加锁(注意是同时读写,同时读没事)。
而且同样是锁,还很讲究,线程读写锁,线程互斥锁,信号量等,适用与不同场景。

论坛徽章:
7
IT运维版块每日发帖之星
日期:2016-05-27 06:20:00IT运维版块每日发帖之星
日期:2016-06-09 06:20:00操作系统版块每日发帖之星
日期:2016-06-12 06:20:00程序设计版块每日发帖之星
日期:2016-06-12 06:20:00操作系统版块每日发帖之星
日期:2016-06-13 06:20:00IT运维版块每日发帖之星
日期:2016-06-17 06:20:002015-2016NBA季后赛纪念章
日期:2016-06-28 17:42:27
5 [报告]
发表于 2017-07-29 19:19 |只看该作者
回复 2# cokeboL

按照你的这种说法,如果是通过非阻塞io实现的单进程。 里面的事件对于一个全局变量的操作也不需要加锁吗?(我用的是libevent实现的时间循环,定时器实现定时任务对全局变量进行写, web服务器读取这个全局变量)

论坛徽章:
7
IT运维版块每日发帖之星
日期:2016-05-27 06:20:00IT运维版块每日发帖之星
日期:2016-06-09 06:20:00操作系统版块每日发帖之星
日期:2016-06-12 06:20:00程序设计版块每日发帖之星
日期:2016-06-12 06:20:00操作系统版块每日发帖之星
日期:2016-06-13 06:20:00IT运维版块每日发帖之星
日期:2016-06-17 06:20:002015-2016NBA季后赛纪念章
日期:2016-06-28 17:42:27
6 [报告]
发表于 2017-07-29 19:31 |只看该作者
回复 3# lxyscls

感谢大神。
post value不依赖pre value.

我看文档是如下,我的类型是char * ; 应该不需要枷锁吧。
  1. 英特尔文档中给出的定义仅允许使用int , long , long long以及未签名的对应类型。 GCC将允许任何长度为1,2,4或8个字节的整型标量或指针类型。
复制代码

其实这个文档还是没怎么看懂。
如果不是x86系统呢,我的平台是mips的mtk,ar934x之类的路由器系统

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
7 [报告]
发表于 2017-07-29 21:43 |只看该作者
我2楼回复简单了
通常在你这个多读写的并发场景,相关的逻辑并不只是读写变量的就完了,你应该还需要这个变量的值做其他事情
这种情况下你需要的是临界区的保护,而不只是简单的变量保护,否则比如
A 线程
get()
action()
B 线程
set()
action
虽然 A 和 B的 get set各自保证了原子性(这里用原子操作和锁都是可以的)
但是 A get() 之后 action() 之前, B 可能进行了 set()
这样 A 再 action() 的时候,用的就是错误的数据了。

原子操作,我只在一种情况下用到,就是一些 Init() 的操作,因为只需要执行一次
只要 compareandswap() 就可以确保 Init() 只执行一次。

我觉得首先你应该搞清楚你需要的只是变量值操作的原子性还是值相关逻辑实现的
临界区的一致性。当然,如果你的值可以容忍我上面举例 A 和 B的不一致情况,那
你原子和锁都可以不用。

还有就是,libevent这种,我没用过,但是我猜它的timer回调的时候应该是在一个
循环里,那样的话,你的定时器回调其实都是单线程的了,也是不需要锁的。当然
这是我的猜测,你需要进一步确认。

论坛徽章:
36
子鼠
日期:2013-08-28 22:23:29黄金圣斗士
日期:2015-12-01 11:37:51程序设计版块每日发帖之星
日期:2015-12-14 06:20:00CU十四周年纪念徽章
日期:2015-12-22 16:50:40IT运维版块每日发帖之星
日期:2016-01-25 06:20:0015-16赛季CBA联赛之深圳
日期:2016-01-27 10:31:172016猴年福章徽章
日期:2016-02-18 15:30:3415-16赛季CBA联赛之福建
日期:2016-04-07 11:25:2215-16赛季CBA联赛之青岛
日期:2016-04-29 18:02:5915-16赛季CBA联赛之北控
日期:2016-06-20 17:38:50技术图书徽章
日期:2016-07-19 13:54:03程序设计版块每日发帖之星
日期:2016-08-21 06:20:00
8 [报告]
发表于 2017-07-29 22:20 |只看该作者
web服务器读取那个变量干嘛?给前端返回?前端需要保持刷新吗?需要前段自己不停刷新或者websocket拉或者推都可以?如果只是显示一下,等用户自己刷的操作
那你锁、原子什么的都可以省了

论坛徽章:
14
水瓶座
日期:2014-06-10 09:51:0215-16赛季CBA联赛之江苏
日期:2017-11-27 11:42:3515-16赛季CBA联赛之八一
日期:2017-04-12 14:26:2815-16赛季CBA联赛之吉林
日期:2016-08-20 10:43:1215-16赛季CBA联赛之广夏
日期:2016-06-23 09:53:58程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-09 06:20:0015-16赛季CBA联赛之上海
日期:2015-12-25 16:40:3515-16赛季CBA联赛之广夏
日期:2015-12-22 09:39:36程序设计版块每日发帖之星
日期:2015-08-24 06:20:002015亚冠之德黑兰石油
日期:2015-08-07 09:57:302015年辞旧岁徽章
日期:2015-03-03 16:54:15
9 [报告]
发表于 2017-07-31 09:34 |只看该作者
本帖最后由 lxyscls 于 2017-07-31 09:36 编辑

回复 6# qianguozheng

如果能保证8byte的原子操作就可以
  1. asm("":::"memory");
复制代码


这种只是为了防止编译器reorder的,x86够了,arm不够,所以还是用gcc提供的api吧

论坛徽章:
14
水瓶座
日期:2014-06-10 09:51:0215-16赛季CBA联赛之江苏
日期:2017-11-27 11:42:3515-16赛季CBA联赛之八一
日期:2017-04-12 14:26:2815-16赛季CBA联赛之吉林
日期:2016-08-20 10:43:1215-16赛季CBA联赛之广夏
日期:2016-06-23 09:53:58程序设计版块每日发帖之星
日期:2016-02-11 06:20:00程序设计版块每日发帖之星
日期:2016-02-09 06:20:0015-16赛季CBA联赛之上海
日期:2015-12-25 16:40:3515-16赛季CBA联赛之广夏
日期:2015-12-22 09:39:36程序设计版块每日发帖之星
日期:2015-08-24 06:20:002015亚冠之德黑兰石油
日期:2015-08-07 09:57:302015年辞旧岁徽章
日期:2015-03-03 16:54:15
10 [报告]
发表于 2017-07-31 09:37 |只看该作者
回复 5# qianguozheng

除非你就一个线程,否则就需要考虑锁的问题
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP