zoukankan      html  css  js  c++  java
  • Interlocked.Increment()函数详解 (转载)

     原文地址

        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+=11:系统从Value的空间取出值,并动态生成一个空间来存储取出来的值;
    
    2:将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束。
    
    
    如果此时有两个Thread ,分别记作threadA,threadB。
    
    1:threadA将Value从存储空间取出,为0;
    
    2:threadB将Value从存储空间取出,为0;
    
    3:threadA将取出来的值和1作加法,并且将和放回Value的空间覆盖掉原值。加法结束,Value=14: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...函数有必要提供。
  • 相关阅读:
    bzoj3237[Ahoi2013] 连通图
    bzoj3075[Usaco2013]Necklace
    bzoj1876[SDOI2009] SuperGCD
    bzoj3295[Cqoi2011] 动态逆序对
    BestCoder#86 E / hdu5808 Price List Strike Back
    bzoj2223[Coci 2009] PATULJCI
    bzoj2738 矩阵乘法
    poj 1321 -- 棋盘问题
    poj 3083 -- Children of the Candy Corn
    poj 2488 -- A Knight's Journey
  • 原文地址:https://www.cnblogs.com/macT/p/12058411.html
Copyright © 2011-2022 走看看