.NET框架中的互锁类及其同步技术

多线程编程中,对共享资源的访问需要特别小心,以避免数据不一致的问题。.NET框架提供了一个名为Interlocked的类,用于在多线程环境下对共享变量执行原子操作。原子操作是指在执行过程中不会被操作系统中断的操作。这意味着,当一个线程正在执行一个原子操作时,其他线程不能中断它。

同步问题

考虑以下代码,它在多线程环境中递增共享变量X

int X = 0; X = X + 1;

从编程的角度来看,X = X + 1是一个原子操作。但是,计算机在内部执行这条指令时,需要进行更多的操作。可以将上述指令分解为三个步骤:

  1. 将变量X的值移动到CPU寄存器。
  2. 在寄存器中递增值。
  3. 将值保存回内存。

假设有两个线程同时执行相同的代码。假设第一个线程处于第二步,即X的值已经递增到1。此时,操作系统停止了第一个线程。第二个线程也在并行运行。第二个线程完成了所有三个步骤,并更新了X的值为1。现在假设第一个线程恢复其操作并执行第三步。在这一步,X的值从0更新为1,这是旧值被更新。这意味着执行上述代码后,X的值将是1,这是不正确的。X的值应该是2。

解决方案

通过使用Interlocked类,可以解决这个问题,并得到预期的值,即2。Interlocked类提供了一个简单的线程安全方法Interlocked.Increment(ref variable),它确保操作系统不会中断递增操作。

使用代码

多线程环境中,对共享变量进行递增、递减和交换操作是非常常见的。如果没有同步,可能无法得到预期的结果。但是通过线程同步,可以得到预期的结果。在下面的部分中,描述了两种情况下的实际实现。

以下代码在没有任何同步的情况下,通过多个线程递增共享变量:

int Number = 0; private void btnWithSync_Click(object sender, EventArgs e) { int TotalThread = 10000; Thread[] IntreLockThread = new Thread[TotalThread]; for (int i = 0; i < TotalThread; i++) { IntreLockThread[i] = new Thread(new ThreadStart(UpdateValue)); IntreLockThread[i].IsBackground = true; IntreLockThread[i].Priority = ThreadPriority.Lowest; IntreLockThread[i].Start(); } for (int i = 0; i < TotalThread; i++) { // Block main thread until all child thread to finish. IntreLockThread[i].Join(); } // Show the current value of Number after incremented by all thread lblTotalValue.Text = Number.ToString(); }

在上述代码中,连续运行了10000个线程来递增共享变量。但是,当所有线程执行完成后,变量的值应该是10000。但是,有时并不是这样。有时,值小于10000(在上次运行时,值为9995)。这是由于线程之间的上下文切换。操作系统可以在递增的中间停止当前执行的线程,然后继续执行操作。这将用旧值替换变量的值。因此,这种方式的递增操作不是原子的。可以使用Interlocked类来解决这个问题。

以下代码通过Interlocked类的帮助,通过多个线程递增共享变量。Interlocked类的Increment方法用于此目的。

int Number = 0; private void brnWithSync_Click(object sender, EventArgs e) { Number = 0; int TotalThread = 10000; Thread[] IntreLockThread = new Thread[TotalThread]; for (int i = 0; i < TotalThread; i++) { IntreLockThread[i] = new Thread(new ThreadStart(UpdateWithInterlock)); IntreLockThread[i].IsBackground = true; IntreLockThread[i].Start(); } for (int i = 0; i < TotalThread; i++) { // Block main thread until all child thread to finish. IntreLockThread[i].Join(); } // Show the current value of Number after incremented by all thread lblTotalValueSync.Text = Number.ToString(); }

与之前一样,连续运行了10000个线程来递增共享变量。执行结束后,得到了预期的结果。变量的值递增到了10000。因此,这种方式的递增操作是原子的,并且完全同步。

Interlocked类支持的操作

Interlocked类除了Increment之外,还支持更多操作。这个类的所有方法都接受第一个参数作为引用类型,以便在操作完成后获取更新后的值。这个类的所有非泛型方法都有重载版本。这里只描述了一个版本。以下是Interlocked类的不同线程安全方法:

Interlocked.Add(ref int intNumber, int value); This method adds the value of two parameters and replaces the first one with the sum of both values as an atomic operation. Interlocked.Increment(ref int intNumber); This method increments the value by 1 and assigns the updated value to itself as an atomic operation. Interlocked.Decrement(ref int intNumber); This method decrement the value by 1 and assigns the updated value to itself as an atomic operation. Interlocked.Read(ref int intNumber); It returns the value of variable specified in parameter. Interlocked.Exchange(ref intNumber1, int intNumber2); The exchange method assigns the value of first parameter with the value of second parameter as an atomic operation. It’s basically a set operation. Someone can think why we should use this function as the new value doesn’t depend on the old one. But it's necessary multi-processor or multi-core machine environment. Interlocked.Exchange(ref T Location, T value) This method is a generic version of exchange method. The functionality is the same as Exchange method. Here it must be reference type. Interlocked.CompareExchange(ref int intNumber1, int intNumber2, int CompareValue); CompareExchange is a conditional assignment method. The first parameter is compared with the last parameter. If both are equal, then the value of the first parameter is replaced by the second parameter. Interlocked.CompareExchange(ref T Location, T Value, T CompareValue); This method is a generic version of CompareExchange method. The functionality is the same as Exchange method. Here T must be reference type.

测试代码

要测试上述代码,请运行应用程序。在点击“无同步运行”按钮运行代码时,请尝试通过打开一个新的不同的应用程序来使CPU保持忙碌,以便发生上下文切换。要使用Interlocked类运行同步代码,请单击“同步运行”,并获取实际结果作为预期结果。

沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485