• 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,
    与之相对应的自减锁函数为:

  • 相关阅读:
    MySQL 配置优化
    django基础之数据库操作
    Python常用内置模块之xml模块
    linux命令总结iostat命令
    springboot接口返回封装与异常控制
    springboot接口访问权限AOP实现
    springboot动态多数据源
    Elasticsearch一些使用笔记(持续更新)
    python语言中的AOP利器:装饰器
    如何使用supervisor管理你的应用
  • 原文地址:https://www.cnblogs.com/byfei/p/6389788.html
Copyright © 2020-2023  润新知