码迷,mamicode.com
首页 > 编程语言 > 详细

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

时间:2015-02-26 23:02:25      阅读:400      评论:0      收藏:0      [点我收藏+]

标签:多线程   全局变量   原子操作   线程冲突   

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

前言

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

正文

1.线程冲突

#include <stdio.h>
#include <stdlib.h>
#include <process.h>
#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***\n");
	//共创建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\n", 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);即可。

运行如下

技术分享

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




本专栏目录

所有内容的目录



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

标签:多线程   全局变量   原子操作   线程冲突   

原文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/43956033

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!