키워드는 volatile
동시에 실행되는 여러 스레드에서 필드를 수정할 수 있음을 나타냅니다. 컴파일러, 런타임 시스템 및 하드웨어에서도 성능상의 이유로 메모리 위치에 읽기 및 쓰기를 다시 정렬할 수 있습니다. 선언된 volatile
필드는 특정 종류의 최적화에서 제외됩니다. 실행의 모든 스레드에서 볼 수 있듯이 일시적 쓰기의 단일 총 순서는 보장되지 않습니다. 자세한 내용은 Volatile 클래스를 참조하세요.
주의
이 volatile
키워드는 종종 다중 스레드 프로그래밍에서 오해되고 오용됩니다. 대부분의 시나리오에서는 대신 더 안전하고 신뢰할 수 있는 대안을 volatile
사용해야 합니다. 최신 .NET은 Interlocked 클래스, lock
문 또는 고급 동기화 기본 형태와 같은 보다 나은 동시성 도구를 제공합니다. 이러한 대안은 volatile
보다 명확한 의미 체계와 더 강력한 보장을 제공합니다. 해당 제한 사항을 완전히 이해하고 적절한 솔루션인지 확인한 드문 고급 시나리오에서만 사용하는 volatile
것이 좋습니다.
비고
다중 프로세서 시스템에서 휘발성 읽기 작업은 프로세서에서 해당 메모리 위치에 기록된 최신 값을 가져오도록 보장하지 않습니다. 마찬가지로, 휘발성 쓰기 작업은 기록된 값이 다른 프로세서에 즉시 표시되도록 보장하지 않습니다.
키워드는 volatile
다음 형식의 필드에 적용할 수 있습니다.
- 참조 형식
- 포인터 형식(안전하지 않은 컨텍스트에서). 포인터 자체는 volatile일 수 있지만, 포인터가 가리키는 대상은 volatile일 수 없습니다. 즉, "volatile에 대한 포인터"를 선언할 수 없습니다.
- 단순한 형식인
sbyte
,byte
,short
,ushort
,int
,uint
,char
,float
및bool
. -
enum
다음 기본 형식 중 하나를 사용하는 형식입니다byte
. ,sbyte
,short
,ushort
int
또는uint
. - 참조 형식으로 알려진 제네릭 형식 매개 변수입니다.
- IntPtr 및 UIntPtr.
이러한 유형의 필드에 대한 읽기 및 쓰기는 원자적으로 보장할 수 없으므로 double
및 long
를 포함한 다른 유형을 volatile
로 표시할 수 없습니다. 이러한 유형의 필드에 대한 다중 스레드 액세스를 보호하려면 클래스 멤버를 Interlocked 사용하거나 문을 사용하여 lock
액세스를 보호합니다.
대부분의 다중 스레드 시나리오에서는 지원되는 형식을 사용하더라도, Interlocked 작업, lock
문 또는 기타 동기화 기본 형식을 사용하는 것이 volatile
를 사용하는 것보다 좋습니다. 이러한 대안은 미묘한 동시성 버그에 덜 취약합니다.
키워드는 volatile
또는 class
의 필드에만 적용할 수 있습니다. 지역 변수를 선언 volatile
할 수 없습니다.
휘발성에 대한 대안
대부분의 경우 다음 대신 volatile
이러한 안전한 대안 중 하나를 사용해야 합니다.
-
Interlocked 연산: 숫자 형식 및 참조 할당에 대한 원자성 연산을 제공합니다. 이는 일반적으로 더 빠르며
volatile
보다 강력한 보장을 제공합니다. -
lock
명령문: 상호 배제 및 메모리 장벽을 제공합니다. 더 큰 중요 섹션을 보호하는 데 사용합니다. -
Volatile 클래스: 키워드보다
volatile
더 명확한 의미 체계를 사용하여 명시적 휘발성 읽기 및 쓰기 작업을 제공합니다. - 상위 수준 동기화 기본 형식: 예를 들어, ReaderWriterLockSlim, Semaphore, 또는 System.Collections.Concurrent의 동시 컬렉션.
키워드는 volatile
할당 이외의 작업에 원자성을 제공하지 않으며 경합 상태를 방지하지 않으며 다른 메모리 작업에 대한 순서 지정 보장을 제공하지 않습니다. 이러한 제한 사항으로 대부분의 동시성 시나리오에는 적합하지 않습니다.
예시
다음 예제에서는 공용 필드 변수를 .로 volatile
선언하는 방법을 보여 있습니다.
class VolatileTest
{
public volatile int sharedStorage;
public void Test(int i)
{
sharedStorage = i;
}
}
다음 예제에서는 보조 또는 작업자 스레드를 만들고 기본 스레드와 병렬로 처리를 수행하는 데 사용하는 방법을 보여 줍니다. 다중 스레딩에 대한 자세한 내용은 관리되는 스레딩을 참조하세요.
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# 구문 및 사용의 최종 소스입니다.
참고하십시오
.NET