免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2515 | 回复: 6

[Linux] linux 线程在未同步的情况下,输出着实没看懂! [复制链接]

论坛徽章:
1
巨蟹座
日期:2014-03-18 23:44:30
发表于 2014-04-05 22:42 |显示全部楼层
  1. /*使用原子锁实现线程同步*/

  2. #include <alsa/iatomic.h>
  3. #include <pthread.h>
  4. #include <stdio.h>

  5. // 定义一个原子变量
  6. static atomic_t g_atomic;
  7. // 定义共享资源
  8. static volatile int g_i = 0;

  9. /* 定义线程处理函数 */
  10. #define atomic_dec_and_test(g_atomic) 1
  11. void *thr1_handle(void *arg)
  12. {
  13.     while (1) {
  14.         if (atomic_dec_and_test(&g_atomic)) {
  15.             printf("in thread %lu g_i = %d\n", pthread_self(), ++g_i);
  16.         }
  17.         atomic_inc(&g_atomic);
  18.         sleep(1);
  19.     }

  20.     return NULL;   
  21. }

  22. void *thr2_handle(void *arg)
  23. {
  24.     while (1) {
  25.         if (atomic_dec_and_test(&g_atomic)) {
  26.             printf("in thread %lu g_i = %d\n", pthread_self(), --g_i);
  27.         }
  28.         atomic_inc(&g_atomic);
  29.         sleep(1);
  30.     }
  31.     return NULL;   
  32. }


  33. /* 主函数 */
  34. int main()
  35. {
  36.     // 主线程初始化原子变量
  37.     //g_atomic = ATOMIC_INIT(0);
  38.     g_atomic.counter = 1;

  39.     pthread_t tid1, tid2;
  40.     if (pthread_create(&tid1, NULL, thr1_handle, NULL) != 0) {
  41.         fprintf(stderr, "create thread1 failed!\n");
  42.         return 1;
  43.     }
  44.     if (pthread_create(&tid2, NULL, thr2_handle, NULL) != 0) {
  45.         fprintf(stderr, "create thread2 failed!\n");
  46.         return 2;
  47.     }

  48.     pthread_join(tid1, NULL);
  49.     pthread_join(tid2, NULL);

  50.     return 0;
  51. }
复制代码
在不使用原子操作来做同步的情况下,程序输出如下:

  1. in thread 3075713856 g_i = 1
  2. in thread 3067321152 g_i = 0
  3. in thread 3067321152 g_i = -1
  4. in thread 3075713856 g_i = 1
  5. in thread 3067321152 g_i = 0
  6. in thread 3075713856 g_i = 1
  7. in thread 3075713856 g_i = 2
  8. [color=Red]in thread 3067321152 g_i = 0
  9. in thread 3075713856 g_i = 3
  10. in thread 3067321152 g_i = 1
  11. in thread 3075713856 g_i = 4
  12. in thread 3067321152 g_i = 2
  13. in thread 3075713856 g_i = 5
  14. in thread 3067321152 g_i = 3
  15. in thread 3075713856 g_i = 6[/color]
  16. in thread 3067321152 g_i = 5
  17. in thread 3067321152 g_i = 4
  18. in thread 3075713856 g_i = 6
  19. in thread 3075713856 g_i = 5
  20. in thread 3067321152 g_i = 3
  21. in thread 3075713856 g_i = 6
  22. in thread 3067321152 g_i = 4
  23. in thread 3067321152 g_i = 3
  24. in thread 3075713856 g_i = 5
  25. in thread 3075713856 g_i = 6
  26. in thread 3067321152 g_i = 4
  27. in thread 3067321152 g_i = 5
  28. in thread 3075713856 g_i = 6
  29. in thread 3075713856 g_i = 7
  30. in thread 3067321152 g_i = 5
复制代码
在输出标红的部分,线程切换时为什么值会出现跳跃增加或减少呢?
按照程序逻辑,在没进入线程一次,只做一次值的加或减呀,怎是怎么个情况? 期待大V指点下

论坛徽章:
4
白羊座
日期:2013-09-17 21:59:30技术图书徽章
日期:2013-10-12 22:16:03白羊座
日期:2013-10-14 11:01:40双子座
日期:2013-12-17 18:26:39
发表于 2014-04-06 00:23 |显示全部楼层
将++g_i(或--g_i)理解为一下三句,就能解释这样的输出产生的原因:
register = memory(g_i);
register = register + 1(或register = register - 1);
memory(g_i) = register;

论坛徽章:
1
巨蟹座
日期:2014-03-18 23:44:30
发表于 2014-04-06 02:10 |显示全部楼层
井蛙夏虫 发表于 2014-04-06 00:23
将++g_i(或--g_i)理解为一下三句,就能解释这样的输出产生的原因:
register = memory(g_i);
register = r ...

谢谢你,井蛙夏虫!
你的意思是说 前++/--这种操作被打断导致上面的情况。可是我不是太明白,请原谅我的愚昧
printf ++g_i 的时候,打印的就是 g_i 所在内存的值,即:
in thread 3075713856 g_i = 1   此时 g_i 内存单元的值为 1
in thread 3075713856 g_i = 2   此时 g_i 内存单元的值为 2
in thread 3067321152 g_i = 0   此时线程切换,--g_i 是取g_i内存的值然后减1,然后将值压入printf的函数栈,怎么就变成0了呢?
in thread 3075713856 g_i = 3
in thread 3067321152 g_i = 1
in thread 3075713856 g_i = 4
期待你的更详细的讲解。

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
发表于 2014-04-06 18:44 |显示全部楼层
2楼都写明白了吧,++和--都不是atomic操作,为什么还是将--g_i理解为一次执行完毕呢?请至少分为三个独立指令,而且执行每个指令后,都可能切换到另外一个线程。
BTW:这算典型的undefined行为,知道不可取就可以了,还深究什么?就好比“malloc一次,free两次,为什么有时候core,有时不core?”,点到即止。

论坛徽章:
1
巨蟹座
日期:2014-03-18 23:44:30
发表于 2014-04-06 21:14 |显示全部楼层
timespace 发表于 2014-04-06 18:44
2楼都写明白了吧,++和--都不是atomic操作,为什么还是将--g_i理解为一次执行完毕呢?请至少分为三个独立指 ...


谢谢指点。
我已知道 ++/-- 不是原子操作,可是我还是不是很明白,能给我做一个深度剖析吗?
in thread 3075713856 g_i = 1           正常
in thread 3067321152 g_i = 0           正常
in thread 3067321152 g_i = -1           正常
in thread 3075713856 g_i = 1           不正常
in thread 3067321152 g_i = 0           正常
in thread 3075713856 g_i = 1           正常
in thread 3075713856 g_i = 2           正常
in thread 3067321152 g_i = 0           不正常
in thread 3075713856 g_i = 3           不正常
in thread 3067321152 g_i = 1           不正常
in thread 3075713856 g_i = 4           不正常
in thread 3067321152 g_i = 2           不正常
in thread 3075713856 g_i = 5           不正常
in thread 3067321152 g_i = 3           不正常
in thread 3075713856 g_i = 6           不正常
从地一个不正常分析。
在本次打印之前,线程3067321152已将g_i的值修改为-1,然后在本次打印的结果却是 1 ,说明本次取到的 g_i 的值为0.   从上面的线程执行情况看,只有在第二行的时候值为0,即在线程 3067321152 中。 难道是in thread 3067321152 g_i = -1还没有及时写入内存,此时发生线程切换,切换到 in thread 3075713856 g_i = 1,这个时候取到的就是 0
不知道这样理解对不对。

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
发表于 2014-04-06 22:15 |显示全部楼层
线程栈相互独立,有各自的寄存器。

/* 从输出1~4行推断,第3行输出之前g_i = 0 ,下面分解第3行和第4行可能执行顺序 */
t1(++)   r1 = g_i     /* r1是t1的寄存器, g_i = 0那么 r1= 0 */
t2(--)     r2 = g_i     /* r2 = 0 */
t1(++)   r1 += 1     /* r1 = 1 */
t2(--)     r2 -= 1      /* r2 = -1 */
t2(--)     g_i = r2     /* g_i = -1 */
t1(++)   g_i = r1     /* g_i = 1 */
/* 不同步,执行是随机的,出现其它结果也不奇怪 */

论坛徽章:
1
巨蟹座
日期:2014-03-18 23:44:30
发表于 2014-04-07 00:44 |显示全部楼层
timespace 发表于 2014-04-06 22:15
线程栈相互独立,有各自的寄存器。

/* 从输出1~4行推断,第3行输出之前g_i = 0 ,下面分解第3行和第4行可 ...


恩,看来我在上一楼的理解是对的。
谢谢你这直观的分析!
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP