2017년 3월 2일 목요일

[국비지원교육/IT실무학원/구로IT학원/C#/ADO.NET/자마린교육추천_탑크리에듀][강좌#13]C#에서의 Thread

쓰레드(Thread) 
쓰레드란 CPU를 이용하는 가장 작은 단위인데 싱글 쓰레드를 사용하면 CPU의 사용 권한을 하나의 쓰레드가 독차지 하게 된다. 지하철 개찰구의 예를들면 개찰구가 하나라면 평상시에는 큰 무리가 아니지만 출퇴근 시간에는 무리가 따를 수 있다. 결국 개찰구를 늘여야 한다는 것이다. 프로그래밍에서 볼 때 개찰구를 늘이는 것이 멀티 쓰레드를 이용하는 것이다. 멀티 쓰레드가 제대로 동작하기 위해서는 CPU가 여러 개 있어야 한다. 보통은 단일 CPU를 사용 할 것이다. CPU는 한번에 하나의 쓰레드를 사용하므로 멀티 쓰레드 프로그램이 실행되는 경우에는 CPU의 사용 시간을 나누어서 각각의 쓰레드에게 나누어 주는 것이다. 결국 개찰구는 늘였지만 표를 파는 곳은 한 군데 인것이다. 

C#에서의 멀티 쓰레드 

C#에서 멀티 쓰레드를 사용하는 방법은 기 정의된 쓰레드 클래스를 사용하면 된다. 결국 배워야 하는 것은 C#에서 멀티 쓰레드를 위한 클래스가 어떤 것이 있고 그 사용법은 어떻게 되는가 이다. 쓰레드를 위한 개체들은 System.Threading 네임스페이스 안에 정의 되어 있다. 

[첫번째 예제] 
using System; 
using System.Threading; 

public class ThreadTest 

public void FirstWork() 

for(int i=0; i < 100; i++) 

Thread.Sleep(1000); //밀리세컨드 단위 
Console.Write("F{0} " ,i); 



public void SecondWork() 

for(int i=0; i < 100; i++) 

Thread.Sleep(1000); 
Console.Write("S{0} " ,i); 



[MTAThread] 
public static void Main() 

ThreadTest t = new ThreadTest(); 

//Thread는 생성자에 ThreadStart형 Delegate를 인자로 받는다. 
Thread first = new Thread(new ThreadStart(t.FirstWork)); 
Thread second = new Thread(new ThreadStart(t.SecondWork)); 

first.Start(); 
second.Start(); 



[예제1-1] 
using System; 
using System.Threading; 
class ThreadTest 

static void Thmethod() 

int id = AppDomain.GetCurrentThreadId(); 
Console.WriteLine("Thread[{0}] Thmethod Method Running",id); 

static void Main() 

int id = AppDomain.GetCurrentThreadId(); 
Console.WriteLine("Main Thread[{0}]",id); 
for(int i=0;i<10;i++) 

Thread th = new Thread(new ThreadStart(Thmethod)); 
th.Start(); 





Thread.Sleep 안에는 해당 Thread가 얼마만큼 쉴 건지에 대한 시간을 밀리 세컨드단위로 지정 한다. 만약 시간을 0으로 설정하면 현재 자신에게 주어진 시간을 다른 쓰레드에게 쓰게 하겠다는 의미이다. 지금 당장은 CPU를 사용 하지 않아도 될 때 다른 쓰레드에게 CPU를 사용 할 기회를 줌으로써 CPU를 효율적으로 이용 할 수 있다. 쓰레드를 쉐게 하는 방법은 Thread.Suspend 를 이용 할 수도 있다. Sleep과의 차이는 Sleep인 경우엔 지정한 시간 만큼 쉰다는 의미지만 Suspend인 경우엔 Resume 메소드를 호출 할 때 까지 쉬게 된다는 것이다.또 다른 차이점은 Sleep 메소드는 자기 자신의 쓰레드만 쉬게 할 수 있다. 반면에 Suspend 는 자기 뿐 아니라 다른 쓰레드도 쉬게 할 수 있다.잠을 자고 있는 Thread가 스스로 깰 수는 없다. 그래서 Suspend로 쉬고 있는 쓰레드는 다른 쓰레드가 Resume을 이용하여 깨울 때 까지 쉬고 있는 것이다. 주의 할 점은 Sleep인 경우는 쓰레드가 즉시 중단 되지만 Suspend로 쉬게 할려면 즉시 중단 되지 않는다. 

[예제2] 
using System; 
using System.Threading; 

public class ThreadTest2 

public bool sleep = false; 
public void FirstWork() 

for(int i=0; i < 10; i++) 

Console.WriteLine("F{0}", i); 
if (i == 5) 

sleep = true; 
Console.WriteLine(""); 
Console.WriteLine("first 쉼..."); 
Thread.CurrentThread.Suspend(); 





class TestMain 

[MTAThread] 
public static void Main() 

ThreadTest2 t = new ThreadTest2(); 
Thread first = new Thread(new ThreadStart(t.FirstWork)); 

first.Start(); 

while(t.sleep == false) {} 

Console.WriteLine(""); 
Console.WriteLine("first를 깨웁니다..."); 
first.Resume(); 


Suspend 메소드로 쓰레드를 잠시 중지하면 Resume 메소드로 다시 동작하게 할 수 있지만 Thread.Abort로 일단 쓰레드를 종료 시키면 다시 되살릴 수 없다.쓰레드가 확실히 종료 되었는지를 살펴보기 위해서는 Thead.Join 메소드를 사용 할 수 있는데 Thread.Join 메소드는 동기적으로 동작하므로 쓰레드가 종료 할 때까지 기다리게 된다. 만일 어떤 이유에서든지 쓰레드가 종료 되지 않는 다면 무한정 기다릴 수 밖에 없다. Thread.Join 메소드 안에 1/1000초 단위로 파라미터를 전달 할 수 있는데이것은 얼마 만큼 Join으로 붙은 쓰레드가 종료 될 때까지 기다릴 수 있는 가에 대한 값이다. 만일 주어진 시간안에 종료되면 true를 반환하고 기다리기를 중지 한다면 false 값을 반환 한다. 

쓰레드가 여러 개 있다면 한 쓰레드가 동작하고 나서 어떤 쓰레드가 동작 할까? 아마도 높은 우선 순위를 가지고 있는 쓰레드가 수행 될 것이다. 각각의 쓰레드는 CPU 사용 권한에 대한 우선 순위가 있는데 우선 순위가 높을수록 CPU사용 권한을 먼저 할당 받는다. 

아래의 예제를 보도록 하자. 

using System; 
using System.Threading; 

public class ThreadTest3 

public bool sleep = false; 
public void FirstWork() 

for(int i=0; i < 10; i++) 

for(int j=0; j < 10; j++) 

Thread.Sleep(100); 
Console.Write(","); 


Console.WriteLine("F{0}", i); 



public void SecondWork() 

for(int i=0; i < 10; i++) 

for(int j=0; j < 10; j++) 

Thread.Sleep(100); 
Console.Write(","); 


Console.WriteLine("S{0}", i); 




class TestMain 

[MTAThread] 
public static void Main() 

ThreadTest3 t = new ThreadTest3(); 
Thread first = new Thread(new ThreadStart(t.FirstWork)); 
Thread second = new Thread(new ThreadStart(t.SecondWork)); 

first.Start(); 
second.Start(); 



위의 예제를 실행해 보면 각각의 쓰레드가 할당 받은 시간이 비슷함을 알 수 있다. 두개의 쓰레드가 우선 순위가 비슷 하니까 아래의 그림과 같은 결과가 나오는 것이다. 

Thread1.jpg

아래의 예는 Join을 이용한 예제이다. 
first.Join() 부분을 주석으로 막은 후 다시 실행 해 보라. 

using System; 
using System.Threading; 

public class ThreadTest2 

public int[] iArray = new int[100]; 
public void CollectData() 

for(int i=0; i <= 20; i++) 

iArray[i] = i+1; 
Console.Write(","); 
Thread.Sleep(500); 




class TestMain 

[MTAThread] 
public static void Main() 

ThreadTest2 t = new ThreadTest2(); 
Thread first = new Thread(new ThreadStart(t.CollectData)); 

first.Start(); 
first.Join(); 

int sum=0; 
for(int i=0; i { 
sum += t.iArray[i]; 


Console.WriteLine(); 
Console.WriteLine("sum = {0}", sum); 




한 쓰레드에 대해 우선순위를 높여 주면 CPU 사용 권한을 우선적으로 갖는 것이다. Thread의 우선 순위와 관련된 프로퍼티가 있는데 열거형 값인 Highest, AboveNormal, Normal, BelowNormal, Lowest중 한 값이다. 아래의 예제를 살펴 보자. 


using System; 
using System.Threading; 

public class ThreadTest3 

public bool sleep = false; 
public void FirstWork() 

for(int i=0; i < 10; i++) 

for(int j=0; j < 10; j++) 

Thread.Sleep(100); 
Console.Write(","); 


Console.WriteLine("F{0}", i); 



public void SecondWork() 

for(int i=0; i < 10; i++) 

for(int j=0; j < 10; j++) 

Thread.Sleep(100); 
Console.Write(","); 


Console.WriteLine("S{0}", i); 




class TestMain 

[MTAThread] 
public static void Main() 

ThreadTest3 t = new ThreadTest3(); 
Thread first = new Thread(new ThreadStart(t.FirstWork)); 
Thread second = new Thread(new ThreadStart(t.SecondWork)); 

first.Priority = ThreadPriority.Lowest; 

first.Start(); 
second.Start(); 



Thread2.jpg

멀티쓰레드 환경인 경우 여러 곳에서 같은 객체의 메소드를 호출 하는 경우에 예기치 않은 결과가 나타날 수 있다. 어떤 메소드의 사용을 한 쓰레드가 끝난 후 다른 쓰레드가 접근하게 하려면 lock 문을 사용 한다. 다음의 예제를 보도록 하자. 


using System; 
using System.Threading; 

public class ThreadTest3 

public string lockString = "Hello"; 
public void Print(string rank) 

//lock을 걸어준 구문은 처음 쓰레드가 끝날때 까지 다른 쓰레드가 접근 금지 
lock (this) 

for(int i=0; i < 10; i++) 

for(int j=0; j < 10; j++) 

Thread.Sleep(100); 
Console.Write(","); 


Console.WriteLine("{0}{1} ", rank, lockString); 




public void FirstWork() 

Print("F"); 


public void SecondWork() 

Print("S"); 



class TestMain 

[MTAThread] 
public static void Main() 

ThreadTest3 t = new ThreadTest3(); 
Thread first = new Thread(new ThreadStart(t.FirstWork)); 
Thread second = new Thread(new ThreadStart(t.SecondWork)); 

first.Start(); 
second.Start(); 




lock 문 이외에 System.Monitor라는 클래스가 있는데 이 Monitor 클래스에는 Enter,Exit 메소드가 있는데 Enter 메소드는 잠금 상황으로, Exit 메소드는 잠금을 해제하는 역할을 한다. 앞에서 작성한 예제를 System.Monitor를 이용하는 예문으로 바꿔 보자… 

using System; 
using System.Threading; 

public class ThreadTest3 

public string lockString = "Hello"; 
public void Print(string rank) 

//lock을 걸어준 구문은 처음 쓰레드가 끝날때 까지 다른 쓰레드가 접근 금지 
Monitor.Enter(this); 
for(int i=0; i < 10; i++) 

for(int j=0; j < 10; j++) 

Thread.Sleep(100); 
Console.Write(","); 


Console.WriteLine("{0}{1} ", rank, lockString); 

Monitor.Exit(this); 


public void FirstWork() 

Print("F"); 


public void SecondWork() 

Print("S"); 



class TestMain 

[MTAThread] 
public static void Main() 

ThreadTest3 t = new ThreadTest3(); 
Thread first = new Thread(new ThreadStart(t.FirstWork)); 
Thread second = new Thread(new ThreadStart(t.SecondWork)); 

first.Start(); 
second.Start(); 

댓글 없음:

댓글 쓰기