11장 스레드
프로세스(Process) - 추적 관리 되는 메모리와 리소스들을 사용하는 실행중인 프로그램 - 고유의 프로세스 식별자(PID)가 할당 - 프로세스별 자원을 할당 - IPC : 파이프, 메시지큐, 세마포, 공유메모리 스레드(Thread) - 프로그램 내의 일련의 작업 단위 - 스레드 단위로 CPU의 작업 제어권을 확보 - 스레드를 프로그래머가 직접 제어 가능 - 스레드들이 자원을 공유 프로세스와 스레드의 관계 및 구조 - 스레드 : 프로세스에 의해 할당된 자원 내에서 특정 명령을 수행하는 모듈 - Call Stack : 스레드 내에서 메소드 호출 순서와 메소드 작업에 필요한 메모리 - TLS(Thread Local Storage) : 스레드 자신의 정보를 저장
System.Threading 네임 스페이스 - 다중 스레드 프로그래밍을 가능하게 하는 여러 형식(클래스, 델리게이트, 열거형을 포함) 형식 설명 Interlocked 다중 스레드에 의해 공유되는 객체들에게 동기화된 접근을 제공(클래스) Monitor locks, wait/signals를 이용해서 스레딩 객체의 동기화를 제공(클래스) Mutex 두 개 이상의 스레드들이 동시에 공유 데이터에 접근할 때 사용되는 동기화 클래스 Thread 스레드를 만들고 제어하며, 속성을 설정하고, 상태를 구하는 클래스 ThreadPool 프로세스 내의 관련 스레드들을 관리(클래스) Timer 지정된 시간 간격으로 메서드를 실행하기 위한 메커니즘을 제공(클래스) ThreadStart 스레드에서 실행되는 메서드를 등록하는 델리게이트 WaitCallBack 스레드 풀에 메서드를 등록하는 델리게이트 TimerCallBack Timer에 의해 호출되는 메서드를 등록하는 델리게이트 ThreadState 스레드가 가질 수 있는 유효한 상태를 지정(열거형) ThreadPriority 스레드 우선순위의 유효한 수준을 지정(열거형)
- Thread 클래스, ThreadPool 클래스, Timer 클래스를 이용 스레드 생성 방법 - Thread 클래스, ThreadPool 클래스, Timer 클래스를 이용 - 스레드를 생성하는 가장 일반적인 방법은 Thread 클래스의 객체 참조를 생성 - 생성자는 ThreadStart 델리게이트 인자를 필요로 한다. - 스레드를 생성하고 시작하는 절차 delegate void ThreadStart(); //ThreadStart 델리게이트 정의 Thread th = new Thread(new ThreadStart(My_fun)); //스레드 생성, My_fun은 스레드로 만들 메서드 th.Start(); //스레드를 시작 - 예) using System; using System.Threading; class Program { public static void Print1() { // 스레드로 만들 정적 메서드 Console.WriteLine("Print1 스레드 시작"); } public void Print2() { //스레드로 만들 일반 메서드 Console.WriteLine("Print2 스레드 시작"); public static int Main(string[] args) { // 주 스레드 Thread thread = new Thread(new ThreadStart(Print1)); thread.Start(); // 스레드 시작 thread = new Thread(new ThreadStart((new Program()).Print2)); Console.WriteLine("주 스레드 시작"); return 0;
ThreadPool 클래스를 이용한 스레드 생성 향상을 제공 - QueueUserWorkItem 정적 메서드를 사용하여 메서드를 큐에 대기시키고 메서드에서 사용할 데이터가 들어 있는 개체를 지정 - 두개의 스레드를 Pool 에 요청하는 작업(아래의 사용법 참조) - 정적 메서드인 QueueUserWorkItem은 WaitCallback 델리게이트를 인자로 하여 Count1, Count2를 Pool에 등록 (아래의 사용법 참조) // Count1 메서드를 큐(Pool)에 대기 ThreadPool.QueueUserWorkItem(new WaitCallback(Count1)); // Count2 메서드를 큐(Pool)에 대기, obj는 메서드에서 사용할 데이터가 있는 객체 ThreadPool.QueueUserWorkItem(new WaitCallback(Count2), obj);
- 예) class Program { public static void Main() { // 정적 메서드 이용한 ThreadPool에 대한 작업 요청, 두 번째 인수는 메서드가 사용할 객체 ThreadPool.QueueUserWorkItem(new WaitCallback(Print1),null); // 일반 메서드를 이용한 ThreadPool에 대한 작업 요청 ThreadPool.QueueUserWorkItem(new WaitCallback((newProgram()).Print2),null); // 주 스레드를 1초 간격으로 대기상태로 전환 for(int i = 0 ; i < 10 ; i++) { Console.WriteLine("주스레드: {0}", i); Thread.Sleep(1000); } private static Random random = new Random(); // 전역 Random 객체 생성 public static void Print1(object obj) { // 스레드로 만들 정적 메서드 for( int i = 0 ; i < 3; i++){ Console.WriteLine("Print1 스레드 :{0} ", i); Thread.Sleep(random.Next(5001)); // 랜덤 시간 동안 sleep public void Print2(object obj) { // 스레드로 만들 메서드 for( int i = 0 ; i < 3; i++) { Console.WriteLine("Print2 스레드 :{0} ", i); Thread.Sleep(random.Next(3001)); // 랜덤 시간 동안 sleep
- 일정한 간격을 두고 특정 메서드를 호출해야 하는 경우 Timer 클래스를 이용한 스레드 생성 - 일정한 간격을 두고 특정 메서드를 호출해야 하는 경우 - Timer 클래스 객체를 생성한 다음에 TimerCallback 델리게이트와 메서드로 전달할 인자, 기간을 지정해 스레드를 생성 delegate void TimerCallback(object obj); // TimerCallback 델리게이트 정의 // 스레드로 실행될 callback 메서드 func, callback에서 사용할 정보가 포함된 객체(null) // callback 지연시간 500, callback 호출 간격 500 Timer timer = new Timer(new TimerCallback(func), null, 500, 500);
- 예) class Program { static int i = 0; public static void Print1(object obj) { // 스레드로 만들 정적 메서드 for( i = 0 ; i < 3; i++){ Console.WriteLine("Print1 스레드 :{0}", i); Thread.Sleep(100); } public void Print2(object obj) { // 스레드로 만들 메서드 for( int i = 0 ; i < 3; i++){ Console.WriteLine("Print2 스레드 :{0}", i); public static void Main() { // 정적 메서드를 이용한 Timer에 대한 작업 요청 Timer timer = new Timer(new TimerCallback(Print1), null, 500, 500); // 일바 메서드를 이용한 Timer에 대한 작업 요청 Timer timer1 = new Timer(new TimerCallback((new Program()).Print2), null, 500, 500); for(int i = 0 ; i < 10 ; i++) { Console.WriteLine("주스레드: {0}", i);
- System.Diagnostics.Process클래스의 GetCurrentProcess 메서드와 Getprocesses 프로세스의 정보와 목록 - System.Diagnostics.Process클래스의 GetCurrentProcess 메서드와 Getprocesses 메서드 이용 class Program { static void Main(string[] args) { Process proc = Process.GetCurrentProcess(); // 현재 실행중인 프로세스 string processName = proc.ProcessName; DateTime startTime = proc.StartTime; int proID = proc.Id; Console.WriteLine("******* 현재 프로세스 정보 *****"); Console.WriteLine(" Process: {0}\n ID: {1}\n 시작시간: {2}\n 메모리:{3}\n", processName, proID, startTime, memory); Console.WriteLine("\n******* 모든 프로세스 정보 *****"); Process [] allProc = Process.GetProcesses(); // 실행중인 모든 프로세스 Console.WriteLine("시스템에서 실행중인 프로세스의 수: {0}", allProc.Length); int index = 1; foreach(Process pro in allProc) { Console.WriteLine("----- {0}번째 프로세스 -----", index++); processName = pro.ProcessName; // 프로세스이름정보 startTime = pro.StartTime; // 프로세스 시작 시간 //프로세스 PID(운영체제 식별번호) proID = pro.Id; memory = pro.VirtualMemorySize; // 메모리 사용량 }
스레드 상태와 목록 - 생성직후상태(Unstarted) : 스레드가 생성된 직후를 의미 - 실행가능상태(Runnable) : 새로운 스레드가 생성되어 Start 메서드를 호출 - 대기상태(Suspended) : 실행중인 스레드에서 Suspend, Sleep 메서드를 호출 - 종료상태(Stopped) : 스레드가 할 일을 모두 마친 상태
스레드의 정보를 제공하거나 설정하는 속성 스레드의 상태를 변경하는 메소드 속성 설명 CurrentThread 현재 실행중인 스레드 값 CurrentContext 스레드가 실행 중인 현재 컨텍스트(개체의 환경을 정의하는 속성) IsAlive 스레드의 실행 여부 Name 스레드의 이름 IsBackground 스레드가 백그라운드 스레드인지의 여부 ThreadState 스레드의 상태 Priority 스레드의 우선순위 스레드의 상태를 변경하는 메소드 속성 설명 Abort 스레드를 종료 GetApartmentState 아파트 상태를 나타내는 ApartmentState 값 GetDomain 현재 스레드가 실행 중인 도메인 Interrupt 대기 스레드 상태에 있는 스레드를 중단 Join 해당 스레드의 실행 종료 때까지 대기 Resume 해당 스레드를 실행 상태로 변경 Sleep 해당 스레드를 특정시간동안 대기상태로 변경 Start Suspend 해당 스레드를 대기상태로 변경 Tostring 스레드 객체를 문자열로 반환
ThreadState는 스레드의 실행 상태를 저장 속성 설명 Unstarted 생성된 스레드에 Thread.Start 메서드가 호출되지 않은 상태 Running 스레드가 차단되지 않고 시작된 상태 WaitSleepJoin Wait, Sleep 또는 Join을 호출한 결과 스레드가 차단된 상태 SuspendRequest 스레드를 일시 중단하도록 요청한 상태 Suspended 스레드가 일시 중단된 상태 AbortRequestede 스레드에서 Thread.Abort 메서드가 호출되었지만 해당 스레드는 자신을 종결시키 려는 보류된 ThreadAbortException을 받지 못한 상태 Stopped 스레드가 중지된 상태
class Program { static void Main(string[] args) { //현재 스레드에 관한 정보를 가져온다. Thread primaryThread = Thread.CurrentThread; Thread.CurrentThread.Name = " TheFirstThread"; Console.WriteLine(" ***** 첫 번째 스레드 정보 *****"); //응용 프로그램의 도메인 이름 Console.WriteLine(" 현 응용프로그램 이름 : {0}" ,Thread.GetDomain().FriendlyName); //스레드 이름 Console.WriteLine(" 스레드 이름 : {0}", primaryThread.Name ); //아파트 상태 Console.WriteLine(" Apt 상태 : {0}", primaryThread.GetApartmentState()); //활동중인지? Console.WriteLine(" 활동중 ? : {0}", primaryThread.IsAlive ); //우선순위 Console.WriteLine(" 우선순위 : {0}", primaryThread.Priority ); //상태 Console.WriteLine(" 스레드 상태 : {0}", primaryThread.ThreadState ); Console.WriteLine(); Thread SecondaryThread = new Thread(new ThreadStart(MyThreadProc)); //우선순위 Highest로 설정 SecondaryThread.Priority = ThreadPriority.Highest; SecondaryThread.Start(); } static void MyThreadProc() { Console.WriteLine(" ***** 두 번째 스레드 정보 ***** ");
Thread.CurrentThread.Name = " TheSecondThread"; Console.WriteLine(" 스레드 이름 : {0}", SecondThread.Name); //아파트 상태 Console.WriteLine(" Apt 상태 : {0}", SecondThread.GetApartmentState() ); //활동중인지? Console.WriteLine(" 활동중 ? : {0}", SecondThread.IsAlive ); //우선순위 Console.WriteLine(" 우선순위 : {0}", SecondThread.Priority ); //상태 Console.WriteLine(" 스레드 상태 : {0}", SecondThread.ThreadState ); Console.WriteLine(); }
} 스레드 우선 순위 - 여러 개의 멀티스레드가 사용될 때 CLR은 단지 하나의 스레드만 CPU를 점유할 수 있도록 제어 - 스레드의 우선권을 설정하기 위하여 ThreadPriority 열거형을 제공 멤버 설명 Highest 가장 높은 우선권 AboveNormal 높은 우선권 Normal 평균의 높은 우선권 BelowNormal 낮은 우선권 Lowest 가장 낮은 우선권 class Program { public static int th; // 여러 객체가 공유 public static void Print1() { for(int i=0;i<3;i++){ Console.WriteLine("첫 번째 스레드 {0}",th); th++; }
public static void Print2() { for(int i=0;i<3;i++) { Console.WriteLine("두 번째 스레드 {0}",th); th++; } public static void Print3() { Console.WriteLine("세 번째 스레드 {0}",th); public static void Main() { Thread th1 = new Thread(new ThreadStart(Print1)); // 첫 번째 스레드 생성 Thread th2 = new Thread(new ThreadStart(Print2)); // 두 번째 스레드 생성 Thread th3 = new Thread(new ThreadStart(Print3)); // 세 번째 스레드 생성 // 스레드 th1는 최우선 순위로 설정 th1.Priority = ThreadPriority.Highest; // 스레드 th3는 최저 순위로 설정 th3.Priority = ThreadPriority.Lowest; th3.Start(); th2.Start(); th1.Start();