class Program { static object lockObj = new object(); static int maxTask = 5; static int currentCount = 0; //假设要处理的数据源 static List<int> numbers = Enumerable.Range(5, 10).ToList(); static void Main(string[] args) { var A = numbers; TaskContinueDemo(); Console.ReadKey(); } private static void TaskContinueDemo() { while (currentCount < maxTask && numbers.Count > 0) { lock (lockObj) { if (currentCount < maxTask && numbers.Count > 0) { Interlocked.Increment(ref currentCount);//以原子操作的形式实现递增 var task = Task.Factory.StartNew(() => { var number = numbers.FirstOrDefault(); if (number > 0) { numbers.Remove(number); Thread.Sleep(1000);//假设执行一秒钟 Console.WriteLine("Task id {0} Time{1} currentCount{2} dealNumber{3}", Task.CurrentId, DateTime.Now, currentCount, number); if (Rand() == 0)//模拟执行中异常 { 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 = 5) { return Math.Abs(Guid.NewGuid().GetHashCode()) % maxNumber; } }
InterLockedIncrement and InterLockedDecrement 实现数的原子性加减。什么是原子性的加减呢? 举个例子:如果一个变量 Long value =0; 首先说一下正常情况下的加减操作:value+=1; 1:系统从Value的空间取出值,并动态生成一个空间来存储取出来的值; 2:将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束。 如果此时有两个Thread ,分别记作threadA,threadB。 1:threadA将Value从存储空间取出,为0; 2:threadB将Value从存储空间取出,为0; 3:threadA将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=1。 4:threadB将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=1。 最后Value =1 ,而正确应该是2;这就是问题的所在,InterLockedIncrement 能够保证在一个线程访问变量时其它线程不能访问。同理InterLockedDecrement。 LONG InterlockedDecrement( LPLONG lpAddend // variable address ); 属于互锁函数,用在同一进程内,需要对共享的一个变量,做减法的时候, 防止其他线程访问这个变量,是实现线程同步的一种办法(互锁函数) 首先要理解多线程同步,共享资源(同时访问全局变量的问题),否则就难以理解。 result = InterlockedDecrement(&SomeInt) 如果不考虑多线程其实就是 result = SomeInt - 1; 但是考虑到多线程问题就复杂了一些。就是说如果想要得到我预期的结果并不容易。 result = SomeInt - 1; 举例说: SomeInt如果==1; 预期的结果result当然==0; 但是,如果SomeInt是一个全程共享的全局变量情况就不一样了。 C语言的"result = SomeInt - 1;" 在实际的执行过程中,有好几条指令,在指令执行过程中,其它线程可能改变SomeInt值,使真正的结果与你预期的不一致。 所以InterlockedDecrement(&SomeInt)的执行过程是这样的 { __禁止其他线程访问 (&SomeInt) 这个地址 SomeInt --; move EAX, someInt; // 设定返回值,C++函数的返回值 都放在EAX中, __开放其他线程访问 (&SomeInt) 这个地址 } 但是实际上只需要几条指令加前缀就可以完成,以上说明是放大的。 你也许会说,这有必要吗? 一般来说,发生错误的概率不大,但是防范总是必要的 如果不考虑多线程 result = InterlockedDecrement(&SomeInt); 就是result = SomeInt - 1; 如果SomeInt==1,result一定==0; 但是,在多线程中如果SomeInt是线程间共享的全局变量,情况就不那么简单了。 result = SomeInt - 1; 在CPU中,要执行好几条指令。在指令中间有可能SomeInt被线程修改。那实际的结果就不是你预期的结果了。 InterlockedDecrement(&SomeInt) 放大的过程,如下: { __禁止其他线程访问 &SomeInt 地址; SomeInt --; /////其他线程不会在这里修改SomeInt值。 !!!!!! mov EAX, SomeInt; //C++ 函数返回值 总放在EAX中。 __开放其他线程访问 &SomeInt 地址; } 实际的CPU执行过程只有几条加前缀的指令(586指令) 你会说,有必要吗? 出错的概率不大,但是错误总是需要防范的。当然可以用其他多线程机制实现,但是都没有这样简洁,所以Interlocked...函数有必要提供。