C# 中的 Volatile 关键字

2025 年 3 月 28 日 | 阅读 4 分钟

在多线程情况下访问类或结构的成员变量时,使用 volatile 关键字很方便。volatile 关键字用于指示编译器变量可能被多个线程访问。当 C# 编译器生成我们的代码时,它会执行一些优化。如果变量未标记为 volatile,编译器将假定一次只有一个线程会访问它。

请记住,volatile 关键字只能应用于以下类型的数据

  • 引用类型
  • 指针类型(在危险情况下)。请注意,虽然它指向的对象可以是 volatile,但指针本身不能。因此,不可能声明 “指向 volatile 的指针”
  • char、float、sbyte、short、ushort、int、uint 和 bool 等类型
  • 枚举类型的 byte、sbyte、short、ushort、int 或 uint 基类型。
  • 已知的泛型类型参数包括 IntPtrUIntPtr,它们是引用类型。

对象和结构变量将需要使用不同的锁定技术。只有当变量属于类或结构时,才能将其声明为 volatile

只需将 volatile 作为变量修饰符之一即可将变量声明为 volatile

以下示例包含两个静态字段。一个字段由字符串类型对象引用组成,另一个字段由 volatile 布尔值类型组成。在 Main 方法中建立新线程后,调用 SetVolatile 方法。这两个字段都在 SetVolatile 中设置。

一个示例,说明如何在多线程环境中使用 volatile 字段。此图中的两个静态字段是一个 volatile 布尔标志 和一个整数数组的引用。整数数组通过方法填充数据,并且布尔标志设置为表示成功。在 Main 方法中建立一个新线程以调用 SetVolatile 方法,我们等待一小段时间,然后确定该过程是否已完成。最后,我们输出整数数组的内容。

示例

输出

Operation is complete. Resulting array:
1 2 3 4 5

当多个线程并发运行时,这可能会导致重大问题。重要的是要注意,volatile 修饰符仅仅指示编译器保持对字段的访问顺序,而不是强制同步加载和存储。通过删除重新排序优化,代码从程序员的角度变得更可预测。

再说一次,由于这是一个复杂的 C# 概念,你们大多数人可能不必担心使用它,尤其是在 ASP.NET 中。但是,我们有时会在 ASP.NET 应用程序中(为了屏幕抓取性能)使用多线程,因此您可能需要熟悉多线程编程并非完全不可能。

在对该主题进行更多研究后,volatile 的三种可移植用途。这是它们的摘要。

  • 将局部变量放在 setjmp 的作用域中,以防止它在

    后回滚。
  • 内存似乎被外部源更改或由于错误的内存映射信号处理程序而导致的。信号处理程序恶作剧。
  • 我们还有一个示例,其中建立并利用工作线程与主线程并发处理数据。

另一个示例显示了如何使用 volatile 标志管理工作线程。在此示例中,Worker 类在循环中运行一些代码,直到 shouldStop 标志设置为 true。Worker 类被创建,工作线程启动,并且 Main 过程等待工作线程变为活动状态,然后发出停止信号。

输出

Main thread: starting CustomWorker thread...
CustomWorker thread: working...
CustomWorker thread: working...
CustomWorker thread: working...
CustomWorker thread: working...
CustomWorker thread: working...
CustomWorker thread: terminating gracefully.
Main thread: CustomWorker thread has terminated.