问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

C# Interlocked 原子操作详解

创作时间:
作者:
@小白创作中心

C# Interlocked 原子操作详解

引用
CSDN
1.
https://blog.csdn.net/oopxiajun2011/article/details/113253410

Interlocked原子操作简介

在多线程编程中,原子操作是一种不可中断的操作,它保证了在执行过程中不会被其他线程干扰。在C#中,Interlocked类提供了一系列的原子操作方法,可以确保对共享变量的访问是线程安全的。

概念理解

可以将原子操作理解为多人共用一碗汤的场景:汤碗里有一把公用的勺子,当有人想喝汤时(或服务员想要加汤时),必须先拿到勺子,其他人则无法使用。只有当当前使用者用完勺子并放回后,其他人才能继续使用。这种机制确保了在任何时刻只有一个线程能够访问共享资源,从而避免了数据竞争和不一致的问题。

Interlocked类的主要功能

  • 防止上下文切换错误:在多线程环境中,当一个线程正在更新可被其他线程访问的变量时,如果不使用原子操作,可能会导致数据损坏或加载错误的值。
  • 确保操作的原子性Interlocked类的方法可以确保对共享变量的读写操作是原子的,不会被其他线程中断。

Interlocked类的主要方法

增加操作

  • Add(Int32, Int32):对两个32位整数求和,并用和替换第一个整数。
  • Add(Int64, Int64):对两个64位整数求和,并用和替换第一个整数。
  • Add(UInt32, UInt32):对两个32位无符号整数求和,并用和替换第一个整数。
  • Add(UInt64, UInt64):对两个64位无符号整数求和,并用和替换第一个整数。

位操作

  • And(Int32, Int32):对两个32位带符号整数进行按位“与”运算,并用结果替换第一个整数。
  • And(Int64, Int64):对两个64位带符号整数进行按位“与”运算,并用结果替换第一个整数。
  • And(UInt32, UInt32):对两个32位无符号整数进行按位“与”运算,并用结果替换第一个整数。
  • And(UInt64, UInt64):对两个64位无符号整数进行按位“与”运算,并用结果替换第一个整数。

比较交换

  • CompareExchange(Double, Double, Double):比较两个双精度浮点数是否相等,如果相等则替换第一个值。
  • CompareExchange(Int32, Int32, Int32):比较两个32位有符号整数是否相等,如果相等则替换第一个值。
  • CompareExchange(Int64, Int64, Int64):比较两个64位有符号整数是否相等,如果相等则替换第一个值。
  • CompareExchange(IntPtr, IntPtr, IntPtr):比较两个平台特定的句柄或指针是否相等,如果相等则替换第一个。
  • CompareExchange(Object, Object, Object):比较两个对象是否引用相等,如果相等则替换第一个对象。
  • CompareExchange(Single, Single, Single):比较两个单精度浮点数是否相等,如果相等则替换第一个值。
  • CompareExchange(UInt32, UInt32, UInt32):比较两个32位无符号整数是否相等,如果相等则替换第一个值。
  • CompareExchange(UInt64, UInt64, UInt64):比较两个64位无符号整数是否相等,如果相等则替换第一个值。
  • CompareExchange<T>(T, T, T):比较指定的引用类型T的两个实例是否引用相等,如果相等则替换第一个。

增减操作

  • Decrement(Int32):以原子操作的形式递减指定变量的值并存储结果。
  • Decrement(Int64):以原子操作的形式递减指定变量的值并存储结果。
  • Decrement(UInt32):以原子操作的形式递减指定变量的值并存储结果。
  • Decrement(UInt64):以原子操作的形式递减指定变量的值并存储结果。

交换操作

  • Exchange(Double, Double):以原子操作的形式,将双精度浮点数设置为指定的值并返回原始值。
  • Exchange(Int32, Int32):以原子操作的形式,将32位有符号整数设置为指定的值并返回原始值。
  • Exchange(Int64, Int64):以原子操作的形式,将64位有符号整数设置为指定的值并返回原始值。
  • Exchange(IntPtr, IntPtr):以原子操作的形式,将平台特定的句柄或指针设置为指定的值并返回原始值。
  • Exchange(Object, Object):以原子操作的形式,将对象设置为指定的值并返回对原始对象的引用。
  • Exchange(Single, Single):以原子操作的形式,将单精度浮点数设置为指定的值并返回原始值。
  • Exchange(UInt32, UInt32):以原子操作的形式,将32位无符号整数设置为指定的值并返回原始值。
  • Exchange(UInt64, UInt64):以原子操作的形式,将64位无符号整数设置为指定的值并返回原始值。
  • Exchange<T>(T, T):以原子操作的形式,将指定类型T的变量设置为指定的值并返回原始值。

增加操作

  • Increment(Int32):以原子操作的形式递增指定变量的值并存储结果。
  • Increment(Int64):以原子操作的形式递增指定变量的值并存储结果。
  • Increment(UInt32):以原子操作的形式递增指定变量的值并存储结果。
  • Increment(UInt64):以原子操作的形式递增指定变量的值并存储结果。

内存屏障

  • MemoryBarrier():同步内存存取,确保当前线程的处理器不会重新排序内存存取指令。
  • MemoryBarrierProcessWide():提供覆盖整个过程的内存屏障,确保来自任何CPU的读写都不能越过该屏障。

位操作

  • Or(Int32, Int32):对两个32位带符号整数进行按位“或”运算,并用结果替换第一个整数。
  • Or(Int64, Int64):对两个64位带符号整数进行按位“或”运算,并用结果替换第一个整数。
  • Or(UInt32, UInt32):对两个32位无符号整数进行按位“或”运算,并用结果替换第一个整数。
  • Or(UInt64, UInt64):对两个64位无符号整数进行按位“或”运算,并用结果替换第一个整数。

读取操作

  • Read(Int64):返回一个以原子操作形式加载的64位值。
  • Read(UInt64):返回一个以原子操作形式加载的64位无符号值。

适用版本

  • .NET 5.0
  • .NET Core 1.0, 1.1, 2.0, 2.1, 2.2, 3.0, 3.1
  • .NET Framework 1.1, 2.0, 3.0, 3.5, 4.0, 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8
  • .NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
  • UWP 10.0
  • Xamarin.Android 7.1
  • Xamarin.iOS 10.8
  • Xamarin.Mac 3.0

使用案例

Exchange方法示例

下面是一个使用Interlocked.Exchange方法实现线程安全资源访问控制的示例:

public class InterlockedExchange
{
    // 0 表示资源未被使用,1 表示资源被占用
    private static int usingResource = 0;
    // 每个线程执行次数
    private const int numThreadIterations = 5;
    // 线程数
    private const int numThreads = 10;

    public static void Main1()
    {
        Thread myThread;
        Random rnd = new Random();
        for (int i = 0; i < numThreads; i++)
        {
            myThread = new Thread(new ThreadStart(MyThreadProc));
            myThread.Name = String.Format($"Thread{i + 1}");
            // 在开始下一个线程之前随机等待一段时间
            Thread.Sleep(rnd.Next(0, 1000));
            myThread.Start();
        }
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine("主线程已执行完");
        Console.ResetColor();
    }

    private static void MyThreadProc()
    {
        while (true)
        {
            // 如果任务拿到资源执行
            if (UseResource()) break;
            // 等待1秒再执行
            Thread.Sleep(1000);
        }
    }

    // 一个简单的拒绝重入的方法
    static bool UseResource()
    {
        // 0 表示方法未在使用
        if (0 == Interlocked.Exchange(ref usingResource, 1))
        {
            Console.WriteLine($"{Thread.CurrentThread.Name} 获得资源");
            // 这里放用于访问非线程安全的资源代码
            // 模仿执行的任务,暂停500毫秒
            Thread.Sleep(500);
            Console.WriteLine($"{Thread.CurrentThread.Name} 释放资源");
            // 释放锁
            Interlocked.Exchange(ref usingResource, 0);
            return true;
        }
        else
        {
            Console.WriteLine($"   {Thread.CurrentThread.Name} 拿资源时,被拒绝");
            return false;
        }
    }
}

执行结果如下:

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号