windows多线程--原子操作

简介:

推荐参考博客:秒杀多线程第三篇 原子操作 Interlocked系列函数

                   原子操作 VS 非原子操作                   

 

原子操作就是不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程切换。                                          本文地址

首先从一个简单的例子来看,1000个线程同时对一个全局变量(初始化为0)做++操作,最后我们期望的这个变量的值是1000,但是有时候结果却事与愿违:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<string>
  #include<iostream>
  #include<process.h>
  #include<windows.h>
  using  namespace  std;
 
volatile  int  g_cnt;
 
unsigned __stdcall threadFun( void  *param)
{
     g_cnt++;
     return  0;
}
 
int  main()
{
     for ( int  j = 0; j < 100; j++)
     {
         g_cnt = 0;
         const  int  threadNum = 1000;
         HANDLE  hth[threadNum];
 
         for ( int  i = 0; i < threadNum; i++)
             hth[i] = ( HANDLE )_beginthreadex(NULL, 0, threadFun, NULL, 0, NULL);
 
         //注意WaitForMultipleObjects每次最多等待MAXIMUM_WAIT_OBJECTS个object;
         //也可以调用1000次WaitForSingleObject
         int  k = threadNum / MAXIMUM_WAIT_OBJECTS;
         for ( int  i = 0; i < k; i++)
             WaitForMultipleObjects(MAXIMUM_WAIT_OBJECTS,
                                    &hth[i*MAXIMUM_WAIT_OBJECTS],
                                    TRUE, INFINITE);
         if (threadNum % MAXIMUM_WAIT_OBJECTS != 0)
             WaitForMultipleObjects(threadNum % MAXIMUM_WAIT_OBJECTS,
                                    &hth[k*MAXIMUM_WAIT_OBJECTS],
                                    TRUE, INFINITE);
 
         for ( int  i = 0; i < threadNum; i++)
             CloseHandle(hth[i]);
 
         if (g_cnt != 1000)
             cout<< "the value of g_cnt: " <<g_cnt<<endl;
     }
}

 

image

为什么会这样呢,因为g_cnt++不是一个原子操作,在vs2010中查看反汇编代码为:

image

对于++操作,编译器把它分为三步:1、从内存中吧g_cnt的值读到寄存器eax,2、eax中的值+1, 3、把eax中的值写会内存

如果有两个线程,线程1执行到第二条语句时,线程2开始执行,那么线程2获取的g_cnt的值还是原来的0(因为线程1还没有执行低三条语句来写回内存),最后g_cnt的值就是1,而不是期望的2;

 

windows系统提供给了一些函数来保证某些操作的原子性:

LONG __cdecl InterlockedIncrement(LONG volatile* Addend); //变量加1

LONG __cdecl InterlockedDecrement(LONG volatile* Addend);//变量减1

 

LONG __cdec InterlockedExchangeAdd(LONG volatile* Addend, LONG Value);//变量加上value

LONG __cdecl InterlockedExchange(LONG volatile* Target, LONG Value);//将value的值 赋值 给target指向的变量

 

以上列出的函数是针对32位的LONG数据的,如果是64位的数据,有其对应的函数,具体可以参考msdn 列出的所有原子操作函数

 

针对上面的问题,我们可以把g_cnt++; 改为 InterlockedIncrement((LONG volatile *)&g_cnt);






本文转自tenos博客园博客,原文链接:http://www.cnblogs.com/TenosDoIt/p/3602426.html,如需转载请自行联系原作者

目录
相关文章
|
8月前
|
安全 Java API
Java多线程编程:使用Atomic类实现原子操作
【4月更文挑战第6天】Java的`java.util.concurrent.atomic`包提供了一系列原子类,如`AtomicInteger`和`AtomicLong`,利用CPU原子指令保证无锁情况下变量更新的原子性,从而实现线程安全。这些类在高并发场景下能避免线程阻塞,提高性能。`AtomicInteger`和`AtomicLong`支持原子地增加、减少和设置值,而`AtomicReference`则适用于原子更新引用对象。尽管原子类具有非阻塞、线程安全和易用等优点,但它们仅保证单个变量的原子性,复杂操作可能仍需传统同步机制。了解其工作原理和局限性,有助于提升并发应用性能。
107 0
|
8月前
多线程并发锁的方案—原子操作
多线程并发锁的方案—原子操作
|
8月前
|
调度 Windows
|
5月前
|
Java Windows
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
【Azure Developer】Windows中通过pslist命令查看到Java进程和线程信息,但为什么和代码中打印出来的进程号不一致呢?
|
5月前
|
安全 Java API
Java多线程编程:使用Atomic类实现原子操作
在Java多线程环境中,共享资源的并发访问可能导致数据不一致。传统的同步机制如`synchronized`关键字或显式锁虽能保障数据一致性,但在高并发场景下可能导致线程阻塞和性能下降。为此,Java提供了`java.util.concurrent.atomic`包下的原子类,利用底层硬件的原子操作确保变量更新的原子性,实现无锁线程安全。
44 0
|
6月前
|
算法 编译器 C++
开发与运维线程问题之在C++的原子操作中memory_order如何解决
开发与运维线程问题之在C++的原子操作中memory_order如何解决
57 2
|
7月前
|
安全 API C++
逆向学习Windows篇:C++中多线程的使用和回调函数的实现
逆向学习Windows篇:C++中多线程的使用和回调函数的实现
228 0
|
8月前
|
存储 安全 Java
java多线程之原子操作类
java多线程之原子操作类
|
8月前
|
设计模式 并行计算 安全
【C/C++ 多线程编程】深入探讨双检锁与原子操作
【C/C++ 多线程编程】深入探讨双检锁与原子操作
259 0
|
8月前
|
Linux C语言
linux c 多线程 互斥锁、自旋锁、原子操作的分析与使用
生活中,我们常常会在12306或者其他购票软件上买票,特别是春节期间或者国庆长假的时候,总会出现抢票的现象,最后总会有人买不到票而埋怨这埋怨那,其实这还好,至少不会跑去现场或者网上去找客服理论,如果出现了付款,但是却没买到票的现象,那才是真的会出现很多问题,将这里的票引入到多线程中,票就被称为临界资源。
92 0