zoukankan      html  css  js  c++  java
  • InterLockedIncrement and InterLockedDecrement

    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...函数有必要提供。

    3示例编辑

    [2]Interlocked.Increment 方法:让++成为原子操作;Interlocked.Decrement 方法让--成为原子操作。
    什么叫原子操作呢。就是不会被别人打断,因为C#中的一个语句,编译成机器代码后会变成多个语句。
    在多线程环境中,线程切换有可能会发生在这多个语句中间。使用Interlocked.Increment,Interlocked.Decrement 可以避免被打断,保证线程安全。
    使用Interlocked.Increment 方法和Interlocked.Decrement 方法MSND例子:
    using System;
    using System.Threading;
    class Test
    {
    static void Main()
    {
    Thread thread1 = new Thread(new ThreadStart(ThreadMethod));
    Thread thread2 = new Thread(new ThreadStart(ThreadMethod));
    thread1.Start();
    thread2.Start();
    thread1.Join();
    thread2.Join();
    // Have the garbage collector run the finalizer for each
    // instance of CountClass and wait for it to finish.
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Console.WriteLine("UnsafeInstanceCount: {0}" +
    " SafeCountInstances: {1}",
    CountClass.UnsafeInstanceCount.ToString(),
    CountClass.SafeInstanceCount.ToString());
    }
    static void ThreadMethod()
    {
    CountClass cClass;
    // Create 100,000 instances of CountClass.
    for(int i = 0; i < 100000; i++)
    {
    cClass = new CountClass();
    }
    }
    }
    class CountClass
    {
    static int unsafeInstanceCount = 0;//不使用原子操作
    static int safeInstanceCount = 0;//使用原子操作
    static public int UnsafeInstanceCount
    {
    get {return unsafeInstanceCount;}
    }
    static public int SafeInstanceCount
    {
    get {return safeInstanceCount;}
    }
    public CountClass()
    {
    unsafeInstanceCount++;
    Interlocked.Increment(ref safeInstanceCount);
    }
    ~CountClass()
    {
    unsafeInstanceCount--;
    Interlocked.Decrement(ref safeInstanceCount);
    }
    }
    不用原子操作例子
    class Program
    {
    static void Main(string[] args)
    {
    for (int loop = 0; loop < 20; loop++)
    {
    sum = 0;
    Thread t1 = new Thread(Thread1);
    Thread t2 = new Thread(Thread2);
    t1.Start();
    t2.Start();
    t1.Join();
    t2.Join();
    Console.WriteLine("sum = " + sum); // sum = 200000 ?
    }
    }
    static int sum;
    static void Thread1()
    {
    for (int i = 0; i < 100000; i++) sum++;
    }
    static void Thread2()
    {
    for (int i = 0; i < 100000; i++) sum++;
    }
    }
    结果:
    /*
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 192361
    sum = 175155
    sum = 200000
    sum = 176024
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 200000
    sum = 176322
    */
    Why the sum is not always 200000?
    The reason is that sum++ is not thread safe (see the possible problem).
    That is the reason we need Interlocked.Increment(), which guarantees the sum is always 200000.
    Thread1 (sum++) Thread2 (sum++)
    --------------------------------------------------------------------
    mov EAX, dword ptr sum .
    inc EAX .
    . mov EAX, dword ptr sum // load sum into a register
    . inc EAX // increase it
    . mov dword ptr sum, EAX // save back
    mov dword ptr sum, EAX
    --------------------------------------------------------------------
    problem: two sum++ are called in different thread,
    but the sum is incremented only once.
    也就是说因为C#中的一个语句,编译成机器代码后会变成多个语句,线程不安全,sum++的第100次操作就被打断了,而在第200000次++操作结束后CPU才轮询到sum++的第100次操作,这时sum的值就是101,
    与之相对应的自减锁函数为:

  • 相关阅读:
    MFC Windows 程序设计>WinMain 简单Windows程序 命令行编译
    AT3949 [AGC022D] Shopping 题解
    CF643D Bearish Fanpages 题解
    CF643C Levels and Regions 题解
    CF241E Flights 题解
    CF671C Ultimate Weirdness of an Array 题解
    CF1592F Alice and Recoloring 题解
    GYM 102452E 题解
    CF494C Helping People 题解
    P5556 圣剑护符
  • 原文地址:https://www.cnblogs.com/byfei/p/6389788.html
Copyright © 2011-2022 走看看