设为首页 加入收藏

TOP

C++拾遗--多线程:原子操作解决线程冲突
2015-07-20 17:15:25 来源: 作者: 【 】 浏览:2
Tags:拾遗 线程 原子 操作 解决 冲突

C++拾遗--多线程:原子操作解决线程冲突

前言

在多线程中操作全局变量一般都会引起线程冲突,为了解决线程冲突,引入原子操作。

正文

1.线程冲突

?

#include 
  
   
#include 
   
     #include 
    
      #include <
     windows.h> int g_count = 0; void count(void *p) { Sleep(100); //do some work //每个线程把g_count加1共10次 for (int i = 0; i < 10; i++) { g_count++; } Sleep(100); //do some work } int main(void) { printf(******多线程访问全局变量演示***by David*** ); //共创建10个线程 HANDLE handles[10]; for (int i = 0; i < 10; i++) { for (int j = 0; j < 10; j++) { handles[j] = _beginthread(count, 0, NULL); } WaitForMultipleObjects(10, handles, 1, INFINITE); printf(%d time g_count = %d , i, g_count); //重置 g_count = 0; } getchar(); return 0; }
    
   
  
运行

?

\

理论上,g_count的最后结果应该是100,可事实却并非如此,不但结果不一定是100,而且每次的结果还很可能不一样。原因是,多个线程对同一个全局变量进行访问时,特别是在进行修改操作,会引起冲突。详细解释:

设置断点,查看反汇编

\

g_count++;被分为三步操作:

1.把g_count的内容从内存中移动到寄存器eax

2.把寄存器eax加1

3.把寄存器eax中的内容移动到内存g_count的地址

三步以后,g_count的值被顺利加1。

cpu执行的时间片内包含多条指令,每条指令执行期间不会被打断,但如果一个操作包含多条指令,则很有可能该操作会被打断。g_count++;就是这样的操作。

g_count被顺利加到100的前提:每次加1,都建立在上一次加1顺利完成的基础上。也就是说,如果上一次加1被打断,这一次的加1就得不到上一次加1的累积效果。自然,最后的结果,多半会小于100。

?

2.原子操作

所谓原子操作,是指不会被线程调度机制打断的操作,操作一旦开始,就得执行到结束为止。原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序是不可以被打乱,或者切割掉只执行部分。原子操作一般靠底层汇编实现。

在头文件winnt.h中提供了很多的原子操作函数,它们使用自加锁的方式,保证操作的原子性,如自增操作

InterlockedIncrement,

函数原型

LONG CDECL_NON_WVMPURE InterlockedIncrement(
_Inout_ _Interlocked_operand_ LONG volatile *Addend
);

其它相关操作,请自行查看头文件。

使用该函数,我们修改线程函数count。修改很简单,只需把g_count++改为InterlockedIncrement((LONG)&g_count);即可。

运行如下

\

显然,在原子操作下,我们肯定是可以得到正确结果的。

?

?


?

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇hdu 2196 computer 树状dp 下一篇(hdu step 5.1.1)A Bug's Lif..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容:

·Redis on AWS:Elast (2025-12-27 04:19:30)
·在 Spring Boot 项目 (2025-12-27 04:19:27)
·使用华为开发者空间 (2025-12-27 04:19:24)
·Getting Started wit (2025-12-27 03:49:24)
·Ubuntu 上最好用的中 (2025-12-27 03:49:20)