C#에서 스레드는 System.Threading.Thread
클래스를 사용하여 생성하고 관리할 수 있습니다.
Thread
클래스의 인스턴스를 생성하고 Start()
메서드를 호출하여 스레드를 시작합니다. 스레드는 실행할 메서드를 인자로 받아 생성됩니다.static void DoSomething() { ... } // 스레드가 실행할 메서드
Thread t1 = new Thread(new ThreadStart(DoSomething)); // Thread 인스턴스 생성
t1.Start(); // 스레드 시작
Join()
메서드를 사용합니다. Join()
메서드는 해당 스레드가 실행을 마치고 완전히 정지할 때까지 호출한 스레드를 블록합니다. Thread.Sleep()
메서드는 인자로 주어진 시간(밀리초)만큼 스레드의 CPU 사용을 멈춥니다.Abort()
메서드를 사용할 수 있습니다. Abort()
가 호출되면 해당 스레드에서 ThreadAbortException
예외가 발생하며, 이를 통해 스레드의 실행을 중단시킬 수 있습니다. 스레드가 Abort()
에 의해 정지될 때까지 Join()
메서드로 대기할 수 있습니다. 하지만 Abort()
는 권장되지 않는 방식입니다.Sleep()
, Wait()
, Join()
과 같은 대기 상태에 있을 때, Interrupt()
메서드를 사용하여 스레드를 깨울 수 있습니다. Interrupt()
가 호출되면 해당 스레드에서 ThreadInterruptedException
예외가 발생합니다. Interrupt()
는 스레드를 강제로 종료하기보다는, 대기 상태에서 벗어나게 하여 정상적인 종료 절차를 수행하도록 유도할 때 사용될 수 있습니다. Interrupt()
호출 후 스레드가 정지할 때까지 Join()
으로 대기할 수 있습니다.스레드는 여러 상태를 가집니다. 주요 상태 변화는 다음과 같습니다:
Thread.Start()
에 의해 이 상태로 진입합니다.Monitor.Wait()
, Thread.Sleep()
, Thread.Join()
메서드에 의해 일시 중단된 상태.Thread.Suspend()
에 의해 일시 중단된 상태 (권장되지 않음).Thread.Abort()
가 호출되어 스레드 종료가 요청된 상태.Thread.Abort()
에 의해 강제로 종료된 상태.ThreadState
열거형을 통해 스레드의 상태를 확인할 수 있습니다.
여러 스레드가 하나의 공유 자원(예: 변수, 파일)에 동시에 접근하여 데이터를 변경하려 할 때 문제가 발생할 수 있습니다. 이를 경쟁 상태(Race Condition)라고 하며, 예상치 못한 결과가 나올 수 있습니다. 이러한 문제를 해결하기 위해 스레드 간 동기화(Synchronization)가 필요합니다.
lock
키워드를 사용하는 것입니다. lock
블록으로 감싼 코드는 한 번에 하나의 스레드만 접근할 수 있도록 보장합니다. lock
은 내부적으로 Monitor
클래스를 사용합니다.private readonly object thisLock = new object(); // 동기화에 사용할 객체
public void Increase()
{
lock (thisLock) // lock 블록
{
count = count + 1; // 이 코드는 한 번에 하나의 스레드만 실행
}
}
lock
블록이 끝날 때까지 다른 스레드는 해당 코드를 실행할 수 없습니다.lock
키워드보다 더 세밀한 제어가 필요할 때 Monitor
클래스를 직접 사용할 수 있습니다. Monitor.Enter()
로 진입하고 Monitor.Exit()
으로 빠져나오며 임계 영역을 설정합니다. Monitor.Wait()
와 Monitor.Pulse()
/PulseAll()
을 사용하여 스레드 간에 신호를 주고받으며 대기 상태를 제어할 수도 있습니다..NET Framework 4.0부터 도입된 태스크(Task)는 스레드보다 고수준의 추상화이며, 비동기 및 병렬 작업을 더 쉽게 관리할 수 있도록 설계되었습니다. 대부분의 경우 Thread
클래스보다 Task
를 사용하는 것이 권장됩니다.
Task
는 반환 값이 없는 비동기 작업을 표현합니다. Task.Run()
메서드를 사용하여 간단하게 작업을 백그라운드에서 실행할 수 있습니다. Task.Wait()
메서드는 해당 태스크가 완료될 때까지 기다립니다.Task<TResult>
는 반환 값이 있는 비동기 작업을 표현합니다. 작업이 완료된 후 Result
속성을 통해 결과를 얻을 수 있습니다. Result
에 접근하면 작업이 완료될 때까지 대기하게 됩니다.Parallel
클래스는 병렬 처리를 쉽게 수행할 수 있도록 돕습니다. Parallel.For()
나 Parallel.ForEach()
와 같은 메서드를 통해 반복 작업을 여러 스레드에서 병렬로 실행하여 성능을 향상시킬 수 있습니다. 병렬 처리는 하나의 작업을 여러 작업자로 나누어 동시에 수행하는 방식입니다.async
및 await
키워드는 비동기 코드를 작성하는 새로운 방식입니다. 주로 I/O 바운드 작업(파일 읽기/쓰기, 네트워크 통신 등)에서 메인 스레드를 블록하지 않고 다른 작업을 수행할 수 있도록 합니다.
async
메서드는 일반적으로 Task
또는 Task<TResult>
를 반환합니다.async
메서드 내에서 비동기 작업(Task
또는 Task<TResult>
반환)의 완료를 기다릴 때 사용합니다. await
를 만나면 해당 메서드의 실행은 일시 중단되고, 호출 스레드는 다른 작업을 수행할 수 있게 됩니다. 비동기 작업이 완료되면 중단되었던 메서드의 실행이 재개됩니다.Task.Delay()
는 Thread.Sleep()
과 유사하게 지정된 시간만큼 대기하지만, 호출 스레드를 블록하지 않는다는 중요한 차이가 있습니다. 따라서 UI 스레드에서 Task.Delay()
를 사용해도 UI가 멈추지 않습니다. .NET은 ReadAsync
, WriteAsync
등 다양한 비동기 API를 제공하며, 이들은 주로 I/O 바운드 작업을 처리합니다.