Creative Commons License

Microsoft .NET

닷넷!시작하기
닷넷! Ver 2.0~
닷넷!스킬업
웹개발
윈폼개발
실용모듈개발
Tip & Tech
하루 한 문법

Microsoft .NET 개발자들을 위한 공간입니다. 기초강의에서 부터 고급 기술 정보 및 팁등을 다루도록 하겠습니다.

.

닷넷!스킬업

닷넷 기술을 조금 더 깊이 다루고자 합니다. 특정 주제를 정하지 않고 이슈 발생 시 마다 체계적으로 정리하여 공유하겠습니다. 이전 자료를 옮겨온 곳이기도 합니다.

[Thread] Thread 메서드에 매개변수 넘기기

작성자 : 박종명
최초 작성일 : 2008-06-16 (월요일)
최종 수정일 : 2008-06-16 (월요일)
조회 수 : 5809

닷넷에서 Thread 를 생성하기 위해서는 ThreadStart 델리게이트를 이용해야 한다.

다음과 같이
Thread t = new Thread(ThreadStart(쓰레드메서드));
t.Start();
 
이렇게 하면 '쓰레드 메서드' 는 주 스레드와 분리된 다른 스레드(Worker Thread) 상에서 실행됨으로 비동기 처리가 가능하다.
 
그런데 이 ThreadStart 델리게이트는 다음과 같이 선언되어 있다.

public delegate void ThreadStart();
 
매개변수를 받을수 없을 뿐더라 반환값 역시 없다. 따라서 쓰레드 메서드는 매개변수를 가질 수 없다는 제약사항이 있다. 그러나 상황에 따라서 쓰레드 메서드에도 변수값 참조가 필요하다. 이때 쓰는 방법이 흔히 전역변수 나 클래스 변수를 가지고 적적히 사용하게 될 것이다.
 
이번 시간에는 쓰레드 메서드에 매개변수를 받을 수 있도록 하는 기법(?)에 대해 알아 본다.
핵심은 사용자 정의 델리게이트를 선언하여 비동기로 델리게이트를 실행하는 것이다.
 
우선 샘플 화면을 보자

GO 버턴을 누르면 주 쓰레드와는 다른 쓰레드에서 비동기로 처리가 시작된다.
쓰레드 메서드가 받아 들이는 매개변수는 터보 기능이라 하여 속도를 2배 올려주는 역할을 하는 매개변수이다
 
코드를 보자.
 

delegate void RunDelegate(int speed);

 

private void button1_Click(object sender, System.EventArgs e)

{                                                           

        int turbo = 2; //전달할 파라메타

        RunDelegate runDelegate = new RunDelegate(this.RunThread);

        runDelegate.BeginInvoke(turbo,null,null);           

}

              

#region Worker Thread 상에서 수행될 메서드(쓰레드 메서드임에도 매개변수를 가진다)

private void RunThread(int turbo)

{             

        for(int speed = 0; speed <= 100000; speed++)

        {

this.textBox1.Text += String.Format("속도 : {0} 으로 달리고 있습니다\r\n",speed*turbo );

        }

}

#endregion

 

쓰레드 메서드는 RunThread 이다.
이 메서드는 turbo 라는 int 형 매개변수를 받아 들인다.
 
그리고 델리게이트를 하나 선언한다.

delegate void RunDelegate(int speed);

이 델리게이트는 int 형 매개변수를 가지도록 한다
 
Go 버턴을 클릭하면 버턴 클릭 이벤트가 발생하며 이 이벤트 에서  다음과 같이 처리한다.

int turbo = 2; //전달할 파라메타

RunDelegate runDelegate = new RunDelegate(this.RunThread);

runDelegate.BeginInvoke(turbo,null,null);    

델리게이트를 생성하면서 참조되는 메서드를 RunThread 로 지정한다.

그리고 델리게이트의 BeingInvoke 를 호출함으로써 RunTrhead 메서드를 비동기 처리한다.

위 코드에서 Thread 객체는 찾아 볼 수 없다.

이것이 쓰레드 인가??????

결론저긍로 말하자면, 명시적으로 Thread 객체를 생성하지 않았을 뿐이지 내부적으로 주쓰레드와는 별도의 호출스택을 가지며 따라서 비동기 처리가 가능하다.

진정 Thread 객체를 사용해야 직성이 풀린다면 아래처럼 사용하던지..

delegate void RunDelegate(int speed);

 

private void button1_Click(object sender, System.EventArgs e)

{                                                                                  

        ThreadStart ts = new ThreadStart(this.InvokeMethod);

        Thread thread = new Thread(ts);

        thread.IsBackground = true;

        thread.Start();                      

}                                                                   

                             

private void InvokeMethod()

{

        int turbo = 2; //전달할 파라메타

        RunDelegate runDelegate = new RunDelegate(this.RunThread);                 

        runDelegate.BeginInvoke(turbo,null,null);

}

 

#region Worker Thread 상에서 수행될 메서드(쓰레드 메서드임에도 매개변수를 가진다)

private void RunThread(int turbo)

{             

        for(int speed = 0; speed <= 100000; speed++)

        {

               this.textBox1.Text += String.Format("속도 : {0} 으로 달리고 있습니다\r\n",speed*turbo );

        }

}

#endregion

처음의 코드를 한번 더 랩핑한 것과 같다.

Thread 객체가 호출하는 메서드는 InvokeMethod 이며 InvokeMethod 는 또다시 RunThread 를 비동기적으로 호출한다. 결국 RunThread  가 비동기로 호출되는 것이다.

단, 여기서 한가지 주의할 것이 있다.

Thread.Start() -> InvokeMethod() -> RunThread () 형태로 실행될 지라도

InvokeMethod 의 쓰레드와 RunThread 의 쓰레드는 서로 다른 쓰레드 이다.


각 메서드 안에

MessageBox.Show("쓰레드 :" + Thread.CurrentThread.GetHashCode().ToString());

로 보면 서로 다른 쓰레드 임을 알 수 있다.

∵Commented by 연습생 at 2008-07-10 오전 11:23:26  
위코드를 복사 후 하면
"크로스 스레드 작업이 잘못되었습니다. 'textBox1' 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다."
에러가 나요... 제가 뭘 추가 안했나요...
∵Commented by 박종명 at 2008-07-10 오전 11:57:13  
위 코드는 닷넷 1.x 환경에서 테스트된 코드였어요.
Visual Studio 2003 에서 테스트 해보면 정상 동작하네요.
그러나 닷넷 2.0 에서 테스트 해보니 크로스쓰레드 문제가 발생하네요.
닷넷프레임워크 2.0으로 버전 업 되면서 좀더 엄격해진 건지..
크로스 쓰레드 문제에 대한 내용은 MSDN과 각종 사이트에 상세히 나와 있으니 참고 바라며,
코드를 다음과 같이 한번 더 Control.Invoke 해주니까 일단 문제는 해결되네요..
private void RunThread(int turbo)
{
for (int speed = 0; speed <= 1000; speed++)
{
string text = String.Format(
"속도 : {0} 으로 달리고 있습니다\r\n", speed * turbo);

this.Invoke(new procControl(this.BindingTextBox), new object[] { text });
}
}

//추가된 코드
protected delegate void procControl(string text);
private void BindingTextBox(string text)
{

this.textBox1.Text += text;

}
∵Commented by 박종명 at 2008-07-10 오전 11:58:46  
그리고 닷넷 2.0에서는 매개변수가 있는 쓰레드 메서드를 실행하기 위해 이처럼 할 필요는 없습니다. 다음과 같이 ParameterizedThreadStart 델리게이트를 사용하면 쓰레드에서 매개변수를
사용할 수 있습니다
System.Threading.ParameterizedThreadStart ts = new System.Threading.ParameterizedThreadStart(t.aaa);
System.Threading.Thread thread = new System.Threading.Thread(ts);
thread.Start("매개변수전달값");

∵Commented by 박종명 at 2008-07-10 오전 11:59:57  
그리고 한가지 더 있습니다 ^^;
역시, 닷넷 2.0에 추가된 BackgroundWorker 를 이용하면 보다 안전하고 효과적으로
비동기 작업을 하실 수 있습니다. 다음의 글을 참고해 주세요~
http://mkexdev.net/Article/Content.aspx?parentCategoryID=1&categoryID=23&ID=263
∵Commented by 연습생 at 2008-07-10 오후 2:53:00  
BackgroundWorker 와 Thread를 이용한 비동기와.. 많은 차이가 있나요...
thread는 공부할 수록 어렵네요 ㅠㅜ
∵Commented by 박종명 at 2008-07-10 오후 4:11:14  
차이점은 상대적인 기준이 있는지라 뭐라 딱 잘라 말씀드릴 순 없을것 같구요..
(기준이라함은 개발자의 개발능력 및 시스템 요구사항, 기존 시스템과의 상호관계 등)
닷넷이 제공하는 BackgroundWorker 를 사용하면 보다 안정적일 수 있겠죠.
그리고 시간이 오래 걸리는 비동기 작업의 경우 '진행 상태 보고' 및 '중간 작업 취소','작업 완료 보고','작업 오류 보고' 등 이벤트를 쉽게 처리할 수 있어 보다 편리하겠죠..
∵Commented by 연습생 at 2008-07-10 오후 5:16:26  
앗.... 약간.. 질문좀 할께요 ㅠㅜ
문제는 해결이 되었지만.. 텍스트에 뿌려주는 것이 현저히 떨어졌어요..
폼이 늘어날수록.. 엄청나게 느려지는데... ㅠㅜ
BackgroundWorker 이것도... 텍스트로 보여지게 바꾸었더니 크로스문제가 생겨서
위에 올려주신것을 응용해서 해결을 하였지만.. 속도가... 현저히..떨어져요...
∵Commented by 박종명 at 2008-07-10 오후 6:07:18  
속도가 어떻게 떨어진다는 것인지요?? 소스와 자세한 설명을 질문/답변 게시판에 올려주세요~
이름
비밀번호
홈페이지
AA <- 왼쪽의 문자를 오른쪽 박스에 똑같이 입력해 주세요