Volatile 类

定义

包含用于执行可变内存操作的方法。

C#
public static class Volatile
继承
Volatile

注解

在多处理器系统上,由于编译器或处理器中的性能优化,当多个处理器在同一内存上运行时,常规内存操作似乎被重新排序。 易失性内存操作阻止对操作进行某些类型的重新排序。 易失性写入操作可防止对线程的早期内存操作重新排序,以在易失性写入之后发生。 易失性读取操作可防止对线程的后续内存操作重新排序,以在易失读取之前发生。 这些操作可能涉及某些处理器上的内存屏障,这可能会影响性能。

例如,请考虑以下具有两个线程和两个 Int32 字段 xy 最初为零的方案:

线程 1 线程 2
x = 1; int y2 = Volatile.Read(ref y);
Volatile.Write(ref y, 1); int x2 = x;

易失性读取和写入会阻止每个线程中的两个操作(例如,由编译器或处理器)重新排序。 无论这些操作在一个线程上相对于另一个线程的实际发生顺序如何,即使在线程可能在不同的处理器上运行的多处理器系统上,可变操作都保证线程 2 不会看到 y2 == 1x2 == 0。 在线程 1 上,对 x 的写入必须出现在对 的易失性写入y之前,在线程 2 上,对 的xy读取必须出现在易失性读取之后。 因此,如果线程 2 看到 y2 == 1,它也必须看到 x2 == 1

但是,请考虑上述方案与操作发生的特定顺序相同的方案:

序列 线程 1 线程 2
1 x = 1; ...
2 Volatile.Write(ref y, 1); ...
3 ... int y2 = Volatile.Read(ref y);
4 ... int x2 = x;

即使对线程 1 上的易失性写入 y 发生在线程 2 的易失性读取 y 之前,线程 2 仍可能看到 y2 == 0。 对 的易失性写入 y 不保证不同处理器上的后续易失读取 y 将看到更新的值。

备注

  • 易失性读取和写入可确保将值读取或写入内存,而不是缓存 (例如,在处理器寄存器) 。 因此,可以使用这些操作来同步对可由另一个线程或硬件更新的字段的访问。
  • Volatile还为某些 64 位类型(如 和 DoubleInt64提供读取和写入操作。 这种 64 位内存上的易失读取和写入操作即使在 32 位处理器上也是原子的,这与常规读取和写入不同。

可变内存操作适用于同步的特殊情况,其中正常锁定不是可接受的替代方法。 在正常情况下,C# lock 语句、Visual Basic SyncLock 语句和 Monitor 类提供了同步数据访问的最简单且最不容易出错的方法,而 Lazy<T> 类提供了一种简单的方法来编写延迟初始化代码,而无需直接使用双重检查锁定。

Volatile.ReadVolatile.Write 方法支持语言不支持的功能。 例如:

  • 某些语言(如 Visual Basic)无法识别易失性内存操作的概念。 类 Volatile 以此类语言提供该功能。

    备注

    调用其中一种方法仅影响单个内存访问。 若要为字段提供有效的同步,对字段的所有访问都必须使用 Volatile.ReadVolatile.Write

  • 在 C# 中,对 volatile 字段使用修饰符可以保证每次访问该字段都是易失性内存操作,但 volatile 修饰符不能应用于数组元素。 Volatile.ReadVolatile.Write 方法可用于数组元素。

方法

Read(Boolean)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(Byte)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(Double)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(Int16)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(Int32)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(Int64)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(IntPtr)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(SByte)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(Single)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(UInt16)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(UInt32)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(UInt64)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read(UIntPtr)

读取指定字段的值。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

Read<T>(T)

从指定的字段读取对象引用。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之后,则处理器无法将其移至此方法之前。

ReadBarrier()

包含用于执行可变内存操作的方法。

Write(Boolean, Boolean)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(Byte, Byte)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(Double, Double)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(Int16, Int16)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(Int32, Int32)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(Int64, Int64)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(IntPtr, IntPtr)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(SByte, SByte)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(Single, Single)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(UInt16, UInt16)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(UInt32, UInt32)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(UInt64, UInt64)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write(UIntPtr, UIntPtr)

将指定的值写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

Write<T>(T, T)

将指定的对象引用写入指定字段。 在需要进行此操作的系统上,插入防止处理器重新对内存操作进行排序的内存屏障,如下所示:如果读取或写入操作在代码中出现在此方法之前,则处理器无法将其移至此方法之后。

WriteBarrier()

包含用于执行可变内存操作的方法。

适用于

产品 版本
.NET Core 1.0, Core 1.1, Core 2.0, Core 2.1, Core 2.2, Core 3.0, Core 3.1, 5, 6, 7, 8, 9, 10
.NET Framework 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, 4.8.1
.NET Standard 1.0, 1.1, 1.2, 1.3, 1.4, 1.6, 2.0, 2.1
UWP 10.0

另请参阅