使用 volatile 關鍵字表示欄位可能被多個同時執行的執行緒修改。 出於效能考量,編譯器、執行時系統甚至硬體可能會重新排列讀寫到記憶體位置。 將欄位宣告為 會 volatile 排除它在某些類型的優化之外。 從所有執行緒來看,無法保證揮發性寫入的單一總順序。 如需詳細資訊,請參閱 Volatile 類別。
謹慎
關鍵詞 volatile 通常會在多線程程式設計中誤解和誤用。 在大多數情況下,請使用更安全且可靠的替代方案,而非 volatile。 新式 .NET 提供更好的並行工具,例如 Interlocked 類別、 lock 語句或更高層級的同步處理基本類型。 這些替代方法提供比 volatile更清楚的語意和更強的保證。 請考慮僅在您完全瞭解其限制並確認其為適當解決方案的罕見進階案例中使用volatile。
備註
在多處理器系統中,揮發性讀取操作並不保證任何處理器能取得該記憶體位置寫入的最新值。 同樣地,揮發性寫入操作也無法保證寫入的值會立即被其他處理器看到。
C# 語言參考資料記錄了 C# 語言最新版本。 同時也包含即將推出語言版本公開預覽功能的初步文件。
文件中標示了語言最近三個版本或目前公開預覽版中首次引入的任何功能。
小提示
欲查詢某功能何時首次在 C# 中引入,請參閱 C# 語言版本歷史的條目。
將關鍵字套用於 volatile 以下類型的欄位:
- 參考型別。
- 指標類型(在不安全上下文中) 雖然指標本身可以是揮發性的,但它指向的物件卻不行。 換句話說,你不能宣告「指向波動性指標」。
- 簡單類型,例如
sbyte、byte、shortushortintuintchar、、float和 。bool -
enum具有下列其中一個基底型別的類型:byte、sbyte、short、ushort、、int或uint。 - 已知為參考型別的泛型參數。
- IntPtr 與 UIntPtr。
你不能標記其他類型,包括 double 和 long,因為 volatile 對這些類型欄位的讀取和寫入無法保證是原子型的。 為了保護多執行緒對這類欄位的存取,請使用類別成員或 Interlocked 透過陳述 lock 式來保護存取。
對於大部分的多線程案例,即使支援的類型,還是偏好使用 Interlocked 作業、 lock 語句或其他同步處理基本類型,而不是 volatile。 這些替代方法較不容易發生細微的並行錯誤。
你只能將關鍵字套用 volatile 到 a class 或 struct的欄位。 你不能宣告區域變數為 volatile。
揮發性替代選項
大多數情況下,請使用以下較安全的替代方案,而非 volatile:
-
Interlocked operations:提供數值類型和參考指定的原子操作。 這些運算通常比 更快且提供更強的保證
volatile。 -
lock敘述:提供相互排除和記憶體屏障。 用它來保護較大且關鍵的區域。 -
Volatile 類別:提供明確的揮發性讀取和寫入作業,其語意比
volatile關鍵詞更清楚。 - 較高層級的同步處理基本類型:例如 ReaderWriterLockSlim、 Semaphore或 來自 System.Collections.Concurrent的並行集合。
這個 volatile 關鍵字除了指派外,並不會提供其他操作的原子性。 它無法防止競賽條件,也無法為其他記憶體操作提供排序保證。 這些限制使得它不適合大部分的並行案例。
下列範例示範如何將公用字段變數宣告為 volatile。
class VolatileTest
{
public volatile int sharedStorage;
public void Test(int i)
{
sharedStorage = i;
}
}
下列範例示範如何建立輔助或背景工作線程,並用來與主要線程平行執行處理。 如需多線程的詳細資訊,請參閱 Managed Threading。
public class Worker
{
// This method is called when the thread is started.
public void DoWork()
{
bool work = false;
while (!_shouldStop)
{
work = !work; // simulate some work
}
Console.WriteLine("Worker thread: terminating gracefully.");
}
public void RequestStop()
{
_shouldStop = true;
}
// Keyword volatile is used as a hint to the compiler that this data
// member is accessed by multiple threads.
private volatile bool _shouldStop;
}
public class WorkerThreadExample
{
public static void Main()
{
// Create the worker thread object. This does not start the thread.
Worker workerObject = new Worker();
Thread workerThread = new Thread(workerObject.DoWork);
// Start the worker thread.
workerThread.Start();
Console.WriteLine("Main thread: starting worker thread...");
// Loop until the worker thread activates.
while (!workerThread.IsAlive)
;
// Put the main thread to sleep for 500 milliseconds to
// allow the worker thread to do some work.
Thread.Sleep(500);
// Request that the worker thread stop itself.
workerObject.RequestStop();
// Use the Thread.Join method to block the current thread
// until the object's thread terminates.
workerThread.Join();
Console.WriteLine("Main thread: worker thread has terminated.");
}
// Sample output:
// Main thread: starting worker thread...
// Worker thread: terminating gracefully.
// Main thread: worker thread has terminated.
}
當你在宣告_shouldStop中加上volatile修飾符時,結果總是相同(類似前述程式碼中的摘錄)。 不過,如果在 _shouldStop 成員上沒有該修飾詞,則行為無法預測。 此 DoWork 方法可能優化成員存取,導致讀取過時資料。 由於多執行緒程式設計的特性,過時讀取次數難以預測。 不同版本的程式會產生一些不同的結果。
C# 語言規格
如需詳細資訊,請參閱<C# 語言規格>。 語言規格是 C# 語法和使用方式的最終來源。