Interlocked.Increment()函数详解 (转载)
class Program
{
static object lockObj = new object();
static int maxTask = ;
static int currentCount = ;
//假设要处理的数据源
static List<int> numbers = Enumerable.Range(, ).ToList();
static void Main(string[] args)
{
var A = numbers;
TaskContinueDemo();
Console.ReadKey();
}
private static void TaskContinueDemo()
{
while (currentCount < maxTask && numbers.Count > )
{
lock (lockObj)
{
if (currentCount < maxTask && numbers.Count > )
{
Interlocked.Increment(ref currentCount);//以原子操作的形式实现递增
var task = Task.Factory.StartNew(() =>
{
var number = numbers.FirstOrDefault();
if (number > )
{
numbers.Remove(number);
Thread.Sleep();//假设执行一秒钟
Console.WriteLine("Task id {0} Time{1} currentCount{2} dealNumber{3}", Task.CurrentId, DateTime.Now, currentCount, number);
if (Rand() == )//模拟执行中异常
{
numbers.Add(number);//因为出现异常,所以这里需要将number重新放入集合等待处理
Console.WriteLine("number {0} add because Exception", number);
throw new Exception();
}
}
}, TaskCreationOptions.LongRunning).ContinueWith(t =>
{//在ContinueWith中恢复计数
Interlocked.Decrement(ref currentCount);
Console.WriteLine("Continue Task id {0} Time{1} currentCount{2}", Task.CurrentId, DateTime.Now, currentCount);
TaskContinueDemo();
});
}
}
}
}
private static int Rand(int maxNumber = )
{
return Math.Abs(Guid.NewGuid().GetHashCode()) % maxNumber;
} }
InterLockedIncrement and InterLockedDecrement 实现数的原子性加减。什么是原子性的加减呢? 举个例子:如果一个变量 Long value =; 首先说一下正常情况下的加减操作:value+=; :系统从Value的空间取出值,并动态生成一个空间来存储取出来的值; :将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束。 如果此时有两个Thread ,分别记作threadA,threadB。 :threadA将Value从存储空间取出,为0; :threadB将Value从存储空间取出,为0; :threadA将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=。 :threadB将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=。 最后Value = ,而正确应该是2;这就是问题的所在,InterLockedIncrement 能够保证在一个线程访问变量时其它线程不能访问。同理InterLockedDecrement。 LONG InterlockedDecrement(
LPLONG lpAddend // variable address
);
属于互锁函数,用在同一进程内,需要对共享的一个变量,做减法的时候,
防止其他线程访问这个变量,是实现线程同步的一种办法(互锁函数)
首先要理解多线程同步,共享资源(同时访问全局变量的问题),否则就难以理解。
result = InterlockedDecrement(&SomeInt)
如果不考虑多线程其实就是 result = SomeInt - ;
但是考虑到多线程问题就复杂了一些。就是说如果想要得到我预期的结果并不容易。
result = SomeInt - ;
举例说:
SomeInt如果==;
预期的结果result当然==;
但是,如果SomeInt是一个全程共享的全局变量情况就不一样了。
C语言的"result = SomeInt - 1;"
在实际的执行过程中,有好几条指令,在指令执行过程中,其它线程可能改变SomeInt值,使真正的结果与你预期的不一致。
所以InterlockedDecrement(&SomeInt)的执行过程是这样的
{
__禁止其他线程访问 (&SomeInt) 这个地址
SomeInt --;
move EAX, someInt; // 设定返回值,C++函数的返回值 都放在EAX中,
__开放其他线程访问 (&SomeInt) 这个地址
}
但是实际上只需要几条指令加前缀就可以完成,以上说明是放大的。
你也许会说,这有必要吗? 一般来说,发生错误的概率不大,但是防范总是必要的
如果不考虑多线程
result = InterlockedDecrement(&SomeInt);
就是result = SomeInt - ;
如果SomeInt==,result一定==;
但是,在多线程中如果SomeInt是线程间共享的全局变量,情况就不那么简单了。
result = SomeInt - ;
在CPU中,要执行好几条指令。在指令中间有可能SomeInt被线程修改。那实际的结果就不是你预期的结果了。
InterlockedDecrement(&SomeInt)
放大的过程,如下:
{
__禁止其他线程访问 &SomeInt 地址;
SomeInt --;
/////其他线程不会在这里修改SomeInt值。 !!!!!!
mov EAX, SomeInt; //C++ 函数返回值 总放在EAX中。
__开放其他线程访问 &SomeInt 地址;
}
实际的CPU执行过程只有几条加前缀的指令(586指令)
你会说,有必要吗? 出错的概率不大,但是错误总是需要防范的。当然可以用其他多线程机制实现,但是都没有这样简洁,所以Interlocked...函数有必要提供。
Interlocked.Increment()函数详解 (转载)的更多相关文章
- malloc 与 free函数详解<转载>
malloc和free函数详解 本文介绍malloc和free函数的内容. 在C中,对内存的管理是相当重要.下面开始介绍这两个函数: 一.malloc()和free()的基本概念以及基本用法: 1 ...
- kzalloc 函数详解(转载)
用kzalloc申请内存的时候, 效果等同于先是用 kmalloc() 申请空间 , 然后用 memset() 来初始化 ,所有申请的元素都被初始化为 0. view plain /** * kzal ...
- Linux中fork()函数详解(转载)
[原创地址]http://blog.csdn.net/jason314/article/details/5640969 [转载地址]http://www.cnblogs.com/bastard/arc ...
- WinMain函数详解(转载)
略加增添与修改! 工具:VC++6.0 系统:win7 64位 在Windows应用程序中,我们可以认为 WinMain() 函数是程序的入口,WinMain()的原型如下: int WI ...
- 【转载】jQuery.extend 函数详解
转载自:http://www.cnblogs.com/RascallySnake/archive/2010/05/07/1729563.html jQuery.extend 函数详解 JQuery的e ...
- 【转载】3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解
原文:3D/2D中的D3DXMatrixPerspectiveFovLH和D3DXMatrixOrthoLH投影函数详解 3D中z值会影响屏幕坐标系到世界坐标系之间的转换,2D中Z值不会产生影响(而只 ...
- 【转载】C语言itoa()函数和atoi()函数详解(整数转字符C实现)
本文转自: C语言itoa()函数和atoi()函数详解(整数转字符C实现) 介绍 C语言提供了几个标准库函数,可以将任意类型(整型.长整型.浮点型等)的数字转换为字符串. int/float to ...
- Linux中fork()函数详解(转载)
linux中fork()函数详解 一.fork入门知识 一个进程,包括代码.数据和分配给进程的资源.fork()函数通过系统调用创建一个与原来进程几乎完全相同的进程,也就是两个进程可以做完全相同的事, ...
- 转载_fread函数详解
fread函数详解 函数原型: size_t fread( void *buffer, size_t size, size_t count, FILE *strea ...
随机推荐
- ubuntu16 安装opencv3.4.2
下载好opencv3.4.2.zip 执行命令: unzip opencv3.4.2.zip 进入解压后的文件夹: cd opencv3.4.2/ 创建编译路径: mkdir release 进入新创 ...
- Oracle 分析函数 over
最近在做一个OA系统的统计模块,里面有个功能需要统计出每天新增的用户和累计新增的用户, 只有一张 用户登录的表(用户登录时间,用户ID,等等),效果图: 分析:1,同一用户在一天之内可以登录多次,在这 ...
- excel表格 筛选 通过mysql语句
1.整理excel表格的数据 类似的 前面有其他符号的 都可以处理. 注意下一步是2个操纵:分别设置左右: 结果: 2.在复制粘贴到excel的时候,会有一些数字被设置成了科学计数法, 例如复制到ex ...
- C语言-switch语句的使用。对文件的输出处理。for循环和if的结合使用。
//函数fun功能:统计字符串中各元音字母的个数,注意:不区分大小写. //重难点:switch语句的使用. #include <stdlib.h> #include <conio. ...
- 健壮的I/O(RIO)
在上篇Unix系统级I/O中,我们介绍了有关在Unix环境下读取和写入文件的函数read和write,也提到了标准I/O在进行网络I/O时的局限性.但是在某些地方,直接使用read和write往往会出 ...
- 无法打开物理文件 XXX.mdf",操作系统错误 5.5(拒绝访问) 的解决办法
用T-SQL命令附加数据库时,出现如下异常信息: 无法打开物理文件 XXX.mdf".操作系统错误 5:"5(拒绝访问.)". (Microsoft SQL Server ...
- [数据库] MariaDB安装及使用
一.安装MariaDB 1.使用官方源安装marisdb 如果使用阿里云的源,目前的版本号为5.5.64.如果想安装最新的10.x版本,则需要使用MariaDB的官方源. 1)配置官方源: 在/etc ...
- 测试环境docker-swarm安装部署
测试环境swarm安装部署 部署前增加监听docker2375端口 centos 增加tcp监听端口 修改/lib/systemd/system/docker.service sed -i ‘s/Ex ...
- docker aufs存储驱动文件系统
Docker aufs存储驱动layer.diff.mnt目录的区别 /var/lib/docker/aufs layer子目录: 镜像.镜像历史列表.容器.容器INIT分别有对应的文件.文件名和di ...
- C# 集合类型学习
如果你掌握了一门语言的数据结构 ,那么你离掌握这门语言 也不远了 1.列表 对于list,值得一提的是 Capacity 属性,使用默认的构造函数 ,让我们用代码来说明 var intList = n ...