windows多线程(三) 原子操作
一、分析上一篇程序的现象
我们先从上一篇文章中的最后一个程序开始分析。
#include <stdio.h>
#include <windows.h>
const unsigned int THREAD_NUM = 10;
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
printf("我是主线程, pid = %d\n", GetCurrentThreadId()); //输出主线程pid
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadFunc, &i, 0, NULL); // 创建线程
}
WaitForMultipleObjects(THREAD_NUM,hThread,true, INFINITE); //一直等待,知道所有子线程全部返回
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
int n = *(int*)p;
Sleep(1000*n); //第 n 个线程睡眠 n 秒
printf("我是, pid = %d 的子线程\n", GetCurrentThreadId()); //输出子线程pid
printf(" pid = %d 的子线程退出\n\n", GetCurrentThreadId()); //延时10s后输出
return 0;
}
看程序的输出:
按照正常情况来看应该是每一行输出两列,但是中间有一行多出了一列,看图中圈出来的地方,pid = 208 的线程输出线程pid后并没有马上退出,而是等到了最后才退出。(可能每次运行的情况不一样,这里只说明这一种情况),这是为什么的。 这里涉及到了线程调度的问题, 说明pid = 208 的线程输出线程pid后操作系统进行了线程调度,cpu资源被其它线程抢占,这个线程直到最后才又重新分配到cpu资源,重新往下执行。
二、原子操作
这里明明是要写原子操作,但是到目前为止,并没有任何地方提及什么是原子操作,不要着急,接下来就慢慢来说。那么什么是原子操作呢?一个操作如果能够不受中断地完成,我们称之为原子操作
我们来看这个程序
#include <stdio.h>
#include <windows.h>
const unsigned int THREAD_NUM = 50;
unsigned int g_Count = 0;
DWORD WINAPI ThreadFunc(LPVOID);
int main()
{
HANDLE hThread[THREAD_NUM];
for (int i = 0; i < THREAD_NUM; i++)
{
hThread[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, NULL); // 创建线程
}
WaitForMultipleObjects(THREAD_NUM, hThread, true, INFINITE); //一直等待,直到所有子线程全部返回
printf(" 总共 %d 个线程给 g_Count 的值加一,现在 g_Count = %d\n", THREAD_NUM, g_Count);
return 0;
}
DWORD WINAPI ThreadFunc(LPVOID p)
{
Sleep(50);
g_Count++;
Sleep(50);
return 0;
}
有一个全局变量 g_Count ,每个线程给这个全局变量加一,照这么来看最后应该输出 50 ,我们看一下程序的输出(每次都可能不一样的结果)
为什么会这样呢??? 明明有 50 个线程都给 g_Count 加一了,为什么输出 46,根源在于 g_Count++; 这条语句上,这里就只有一条c++语句,按理说不应该有问题,其实不然,现在,在这里打下断点,开始调试,打开反汇编窗口(Vs编译器快捷键 Alt+8),如下图
可以看到,这一条c++语句,被分成了三条汇编语句,先是把 g_Count 的值给寄存器 eax,然后寄存器 eax 的值加一,再把 eax 的值给 g_Count ,这样就完成一次 g_Count++ 操作。出问题的原因就在于,在这几条汇编语句执行的过程中发送了线程切换,比如,A线程刚执行完 add eax,1 还没有把 eax的值给 g_Count,这时B线程开始执行,把 g_Count 原先的值又存入 eax,这就修改了 eax 中A线程计算好的值。
因此在多线程环境中对一个变量进行读写时,我们需要有一种方法能够保证对一个值的递增操作是原子操作——即这个操作不可以被打断性,一个线程在执行原子操作时,其它线程必须等待它完成之后才能开始执行该原子操作。Windows系统为我们提供了一些以Interlocked开头的函数来完成这一任务。这里只是介绍原子操作的概念,这和线程同步息息相关,但是这些以 以Interlocked 开头的函数我们基本不用,就不一一介绍了,感兴趣的可以自己去了解。
windows多线程(三) 原子操作的更多相关文章
- windows多线程编程星球(一)
以前在学校的时候,多线程这一部分是属于那种充满好奇但是又感觉很难掌握的部分.原因嘛我觉得是这玩意儿和编程语言无关,主要和操作系统的有关,所以这部分内容主要出现在讲原理的操作系统书的某一章,看完原理是懂 ...
- 总结windows多线程同步互斥
windows多线程同步互斥--总结 我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同 ...
- windows多线程同步互斥--总结
我的windows多线程系列文章: windows多线程--原子操作 windows多线程同步--事件 windows多线程同步--互斥量 windows多线程同步--临界区 windows多线程同步 ...
- Windows多线程多任务设计初步(转)
Windows多线程多任务设计初步 [前言:]当前流行的Windows操作系统,它能同时运行几个程序(独立运行的程序又称之为进程),对于同一个程序,它又可以分成若干个独立的执行流,我们称之为线程,线程 ...
- windows多线程同步--临界区
推荐参考博客:秒杀多线程第五篇 经典线程同步 关键段CS 关于临界区的观念,一般操作系统书上面都有. 适用范围:它只能同步一个进程中的线程,不能跨进程同步.一般用它来做单个进程内的代码快同步,效率 ...
- windows多线程接口介绍和使用
一windows多线程接口: 1 创建线程 CreateThread 与 _beginthreadex都可以实现创建线程,两个函数的参数 相同, HANDLEWINAPICreateThread( L ...
- Windows多线程编程入门
标签(空格分隔): Windows multithread programming 多线程 并发 编程 背景知识 在开始学习多线程编程之前,先来学习下进程和线程 进程 进程是指具有一定独立功能的程序在 ...
- windows多线程没那么难
windows多线程没那么难 作者:vpoet mail:vpoet_sir@163.com 上一博文中我们引入了CreateThread()多线程编程一个简单的例子,事实上我说windows 多线程 ...
- Windows多线程
//简单的引出多线程是肿么回事儿....当点击下载的时候,下载内容还没结束也可以点击资源库,其实这就用了另一个线程,弹出“下载完成”对话框的时候,没有点击确定是不能点击主页面内容的,这就是用----- ...
- windows phone 三种数据共享的方式(8)
原文:windows phone 三种数据共享的方式(8) 本节实现的内容是数据共享,实现的效果描述:首先是建立两个页面,当页面MainPage通过事件导航到页面SecondPage是,我们需要将Ma ...
随机推荐
- c++ 分配与释放内存
教学内容: calloc分配内存 calloc与malloc的区别 memset函数初始化内存 free释放动态分配的内存 一.calloc函数分配内存 void *calloc( size_t nu ...
- pandas:对字符串类型做差分比较
1. 问题需求 某种行为最常发生时段.最少发生时段与X天前是否一致 需求变形:判断上下行数据是否一致 2. 预备知识 2.1 Series.ne(Series) 判断两个Series是否相等 impo ...
- 如何在忘记mysql的登录密码时更改mysql登录的密码(window及linux)
最近一直在边学习边开发java项目,理所当然的就少不了跟数据库打交道了,但是有时候就会脑子一短路,把mysql的登录密码给忘记了,这个时候我们又很急切的需要进到数据库中查看数据,那这个时候要怎么才能改 ...
- 客户端传入数据的校验-RestController进阶
使用Hibernate Validator进行数据校验 Bean Validation注解(需要加入相关依赖,在SpringBoot中可以直接使用,SpringBoot会帮我们直接加入) @Null ...
- 如何掌握 Kubernetes ?系统学习 k8s 的大纲一份
深度剖析 Kubernetes 深度剖析 k8s 如何学习 Kubernetes ?如何入门 Kubernetes? 为了帮帮初学者,2018 年 InfoQ 旗下(就是你知道的那个 InfoQ 哇) ...
- [2017 ACL] 对话系统
Long Papers [Domain adaptation ] 1. Adversarial Adaptation of Synthetic or Stale Data ( Cited by 14 ...
- linux 安装配置zookeeper脚本
#!/bin/bash # automatic install zookeeper echo "========= Start to install zookeeper ========== ...
- 机器学习算法 --- Pruning (decision trees) & Random Forest Algorithm
一.Table for Content 在之前的文章中我们介绍了Decision Trees Agorithms,然而这个学习算法有一个很大的弊端,就是很容易出现Overfitting,为了解决此问题 ...
- Django FBV/CBV、中间件、GIT使用
s5day82 内容回顾: 1. Http请求本质 Django程序:socket服务端 a. 服务端监听IP和端口 c. 接受请求 \r\n\r\n:请求头和请求体 \r\n & reque ...
- Go文件右键编译
辛辛苦苦写好了.go文件 发现编译还得敲命令才行,或许配置一个好用点的IDE环境可以解决 但是有时候实在不想开IDE 于是在右键添加了一个编译功能 首先保证go相关的环境变量配置正确 Windows ...