volatile關鍵詞表示欄位可能由同時執行的多個線程修改。 基於效能考慮,編譯程式、運行時間系統,甚至是硬體可能會重新排列記憶體位置的讀取和寫入。 宣告 volatile 的欄位會從特定類型的優化中排除。 無法保證從執行的所有線程看到單一揮發性寫入的總順序。 如需詳細資訊,請參閱 Volatile 類別。
謹慎
關鍵詞 volatile 通常會在多線程程式設計中誤解和誤用。 在大部分情況下,您應該使用更安全且更可靠的替代方案,而不是volatile。 新式 .NET 提供更好的並行工具,例如 Interlocked 類別、 lock 語句或更高層級的同步處理基本類型。 這些替代方法提供比 volatile更清楚的語意和更強的保證。 請考慮僅在您完全瞭解其限制並確認其為適當解決方案的罕見進階案例中使用volatile。
備註
在多處理器系統上,揮發性讀取作業不保證取得任何處理器寫入該記憶體位置的最新值。 同樣地,揮發性寫入作業並不保證寫入的值會立即顯示給其他處理器。
volatile關鍵字可以套用至這些類型的欄位:
- 參考型別。
- 指標類型(在不安全上下文中) 請注意,雖然指標本身可以是動態的,但它指向的對象無法。 換句話說,您無法宣告「揮發性指標」。
- 簡單類型,例如
sbyte、byte、shortushortintuintchar、、float和 。bool -
enum具有下列其中一個基底型別的類型:byte、sbyte、short、ushort、、int或uint。 - 已知為參考型別的泛型參數。
- IntPtr 與 UIntPtr。
其他類型,包括 double 和 long,無法標記為 volatile,因為無法保證對這些類型的讀取和寫入操作是原子的。 若要保護這些欄位類型的多線程存取,請使用 Interlocked 類別成員,或使用 lock 語句保護存取。
對於大部分的多線程案例,即使支援的類型,還是偏好使用 Interlocked 作業、 lock 語句或其他同步處理基本類型,而不是 volatile。 這些替代方法較不容易發生細微的並行錯誤。
關鍵詞volatile只能套用到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.
}
在volatile中新增修飾詞至_shouldStop宣告後,您將始終得到相同的結果(如前面代碼片段中顯示的那樣)。 不過,如果在 _shouldStop 成員上沒有該修飾詞,則行為無法預測。
DoWork方法可能會優化成員存取,進而讀取過時的數據。 由於多線程程式設計的性質,過時讀取的數目無法預測。 程式的不同執行會產生一些不同的結果。
C# 語言規格
如需詳細資訊,請參閱<C# 語言規格>。 語言規格是 C# 語法和使用方式的最終來源。