C# BackgroundWorker Tutorial




Using BackgroundWorker

Visual Studio 2008 에서 designer view 의 toolbox 를 보면 BackgroundWorker 콤포넌트가 있다. (.Net 2.0 부터 지원)




Fist step
toolbox 에서 "BackgroundWorker" 를 선택한다.  그러면 윈도우 하단에 Backgroundworker 객체가 생길것이다.




Second step
윈도우 하단 회색 부분에 생성된 Backgroundworker 객체를 선택하면 우측에 Property 창을 볼 수 있다.

Third step
Property 창에서 번개 모양 아이콘을 선택하자 (번개 모양 아이콘은 Microsoft 에서 이벤트들을 위한 아이콘이다.)
Backgroundworker 는 event-driven(이벤트 구동) 방식으로 동작한다. 즉, 이벤트가 발생되면 해당 이벤트에 해당하는 동작을 수행하게 되어있다. 따라서 우리는 Backgroundworker 를 사용하기 위하여 필수적인 이벤트를 만들어야 한다.

Fourth step
4번째 단계는 Backgroundworker 의 이벤트 중에 DoWork 를 더블클릭하여 이벤트 핸들러를 만든다. 이제부터 수동으로 코딩을 해줘야 한다. 즉, DoWork 이벤트를 더블클릭하면 디폴트로 "backgroundWorker1_DoWork" 메소드를 만들게 되고 이 메소드에 실질적인 작업을 구현해주게 된다.

※ 위 UI 작업을 통한 Backgroundworker 생성은 직접 손으로 타이핑하여 구현하는 것 보다 쉽다 (정말?!...)



Adding DoWork

DoWork 의 메소드는 보통의 이벤트 핸들러와 유사하다. C# 코드를 보면 backgroundWorker1_DoWork 메소드를 볼 수 있는데 이 메소드는 DoWork 이벤트를 더블클릭 했을 때 자동으로 생성된 메소드다. 테스트를 위해 Thread.Sleep 커맨들를 넣어 보자. Thread.Sleep 메소드는 Backgroundworker 의 실행을 멈추게 할 것이다. 그러나 전체 CPU를 사용하는 것은 아니다. (cf. C# Sleep Method Pauses Programs - dotnetperls.com)





Properties

다음에 설명은 절대적인 내용은 아니지만 Argument, Result, RunWorkerAsync 메소드를 강조하고 싶다. Backgroundworker 의 property 들은 반드시 어떻게 사용되는지 알 고 있어야 한다. 다음은 코딩할 때 참조할 수 있는 각 property 에 대한 설명이다.

DoWorkEventArgs e
e.Argument 와 e.Result 를 포함하고 있다.
즉, DoWorkEventArgs e 는 e.Argument, e.Result 에 접근하는데 사용되어 진다.

e.Argument
RunWorkerAsync 에 의해서 전달받은 parameter reference 를 얻기 위해서 사용되어 진다.

e.Result
Backgroundworker 처리에 과정을 체크할 수 있다.

backgroundWorker1.RunWorkerAsync (object)
worker 스레드에서 작업 시작을 하기위해 호출되어진다.



Executing code

Backgroundworker 에 argument 와 return value 로  명령들(정보들)을 전달해 줄 수 있다. 다음의 테스트 코드는 argument 를 전달해 주는 방법과 Backgroundworker 를 invoke 시키는 방법, 스레드의 결과를 전달받는 방법을 보여주고 있다.




예제 argument 의 생성
위 코드에서 TestObject  는 예제 객체이다. 아마도 여러분의 프로그램에서는 더 중요하고 복잡한 정보가 담겨질 것이다. 위에선 C# collection initializer 문법이 사용되었다.

어디서든 호출될 수 있다.
여러분의 코드 어디에서든 RunWorkerAsync 를 호출할 수 있다. 예제에선 생성자에서 호출하고 있지만 다른 어디에서든 호출할 수 있다.



Implementation DoWork

여러분이 원하는 로직으로 DoWork 의 메소드를 작성할 수 있다. DoWork 의 메소드(이벤트 핸들러) 에서 구현부와 리턴값으로 DoWorkerEvnetArgs 를 사용하라. 아래의 코드에서는 argument 와 result 를 RunWorkerAsync 호출로 연결시키고 있다. 기억하라!! TestObject 는 RunWorkerAsync 에 파라미터로 전달받은 것이고, e.Argument 로 받게 된다. 또한 반드시 캐스팅 연산을 해주어야 한다.



※ e.Argument 는 항상 RunWorkerAsync 에 의해 background worker 에 전달한 것을 포함하고 있다. 간단히 캐스팅을 통해 원래의 타입으로 변환하여 사용할 수 있다.
 또한 DoWork 의 이벤트 핸들러 메소드 안에서 처리한 결과를 e.Result 에 담아 리턴 시키도록 하자!!



Using RunWorkerCompleted

앞서 생성한 backgroundworker1 아이콘을 클릭한 후 우측의 번개 모양 아이콘을 선택하면 이벤트 목록이 나온다. 거기서 RunWorkerCompleted 이벤트를 사용할 수 있다. RunWorkerCompleted 를 더블클릭하자. 
backgroundWorker1_RunWorkerCompleted 라고 디폴트로 이벤트 핸들러가 생성된다. 이 메소드에 argument 를 받는 코드를 넣어준다.





Tips

Backgroundworker 는 Windowns Form 안에서 스레드들을 구조적으로 overlay 시킨 것으로 매우 직관적이다.
사용하는 단계를 다시 한번 언급한다면,

First, call RunWorkerAsync with an argument.
Backgroundworker 안의 메소드에 어떠한 argument 라도(null 포함) 전달할 수 있다. 객체 상속 관계에 속한 객체도 전달 가능하다.(다형성)

Second, custom processing is run.
DoWork 의 메소드에서 여러분의 코드를 실행시킬 수 있다.

Third, it finished.
여러분이 작성한 처리가 완료되면, RunWorkerCompleted 가 호출된다. 이 메소드 안에서 스레드에서 처리한 (DoWork 의 메소드 에서 처리한) 결과를 전달받을 수 있다. 이런 방법으로 여러분이 작성한 Backgroundworker 객체는 다른 스레드에 있는 객체를 수정한다.



Summary

위에서 C# 언어를 통해 Backgroundworker 컨트롤(컴포넌트) 를 어떻게 사용하는지 살펴보았다. 일반적으로 여러분은 많은 스레들 사용해야 할 때, ThreadPool 사용을 선호한다. 스레드들이 항상 유용한 것은 아니다.(I/O operation 등등). 멀티 코어 시스템에서 우리는 스레드들의 사용이 필요로 하고, Backgroundworker 의 사용은 훌륭한 선택이 될 것이다.


Backgroundworker 클래스 멤버를 살펴보자





메소드와 프로퍼티, 이벤트들을 연관있는 것끼리 묶어 봤다.

RunWorkerAsync - DoWork
RunWorkerAsync 메소드는 백그라운드 작업을 실행 시키는 메소드다. 즉, 스레드를 시작시킨다.
RunWorkerAsync 메소드가 호출되면 DoWork 이벤트가 발생된다. 따라서 DoWork 이벤트의 이벤트 핸들러에 백그라운드에서 처리할 작업을 정의해주면 된다.


WorkerReportsProgress - ReportProgress - ProgressChanged
1. WorkerReportsProgress 프로퍼티
Backgroundworker 객체가 처리작업에 대한 보고(report)를 할 수 있는지에 대해 설정하거나 얻어온다.
default 값은 false 이다. 이 값을 true 로 설정해야 ProgressChanged 이벤트를 발생시킬 수 있다.

2. ReportProgress 메소드
ProgressChanged 이벤트를 발생 시킨다. 매개변수로 Backgroundworker 객체의 작업 완료 정도를 0 에서 100 까지 값을 갖는 퍼센테이지 값을 전달한다. 또한 object 타입으로 RunWorkerAsync 로 전달한 상태 객체값을 전달한다.

3. ProgressChanged 이벤트
ReportProgress 메소드가 호출되었을 때 발생하는 이벤트 이다.


WorkerSupportsCancellation - RunWorkerCompleted
1. WorkerSupportsCancellation 프로퍼티
Backgroundworker 객체가 스레드 취소 기능을 설정하거나 설정값을 얻는다. default 값은 false 이다. 이 값을 true 로 설정해야 RunWorkerCompleted 이벤트가 발생된다.

2. RunWorkerCompleted 이벤트
스레드의 완료, 취소, 예외가 발생했을 때 발생하는 이벤트 이다. 이 이벤트의 매개변수인 RunWorkerCompletedEventArgs 객체를 통해 작업이 중되 취소되었는지, 예외가 발생했는지 알 수 있다.


CancellationPending
Backgroundworker 스레드를 소유한 응용프로그램에서 스레드에 작업 취소를 요청했는지에 대한 설정값을 얻는다.



ProgressChanged



ReportProgress 메소드의 인자로 전달한 값을 ProgressChangedEventArgs 객체를 통해 사용할 수 있다.



RunWorkerCompleted



RunWorkerCompletedEventArgs 객체를 통해 이벤트 정보를 얻을 수 있다.
Cancelled : 스레드의 취소 요청에 의해 스레드가 취소 되었는지에 대한 bool 값
Error        : 스레드의 예외 발생에 의한 스레드가 종료 되었는지에 대한 Exception 타입의 값
Result      : 스레드가 정상종료 되었을 때 object 값







BackgroundWorker 스레드의 실행

1. 다른 스레드 즉, BackgroundWorker 스레드에서 실행할 이벤트 처리기를 생성한다. 아래 코드에서는 threadWaitPermit_DoWork 이벤트 핸들러를 생성하였다.

BackgroundWorker threadWaitPermit = new BackgroundWorker();
threadWaitPermit.DoWork += new System.ComponentModel.DoWorkEventHandler(threadWaitPermit_DoWork);

2. 메인 스레드에서 BackgroundWorker 스레드를 실행시키기 위해서 RunWorkerAsync 메소드를 호출한다. RunWorkerAsync 메소드를 호출하면 위에서 DoWork 이벤트에 등록한 이벤트 핸들러가 실행된다.

threadWaitPermit.RunWorkerAsync(_evtArgs);

RunWorkerAsync 메소드 호출 시 전달한 매개변수는 DoWork 이벤트 핸들러의 메소드에서 적절한 형변환을 거쳐 사용할 수 있다. 매개변수 DoWorkerEventArgs 의 Argument 매개변수가 전달받은 매개변수 이다. 또한 결과 값을 Result 프로퍼티에 넣어준다. 보통 Argument 매개변수값을 넣어주면 좋다.

private void threadWaitPermit_DoWork(object sender, DoWorkEventArgs e)
{
    CustomEventArgs args = e.Argument as CustomEventArgs;
    ...
    e.Result = args;
}



BackgroundWorker 스레드의 취소

BackgroundWorker 스레드를 생성한 스레드에서 BackgroundWorker 스레드의 동작을 취소 시킬 수 있다.

1. BackgroundWorker 의 WorkerSupportsCancellation 프로퍼티값을 true 로 설정한다.

threadWaitPermit.WorkerSupportsCancellation = true;

2. BackgroundWorker 의 스레드를 취소 하기 위해 CancelAsync 메소드를 호출한다. 위에서 WorkerSupportsCancellation 프로퍼티값을 true 로 설정하지 않은 채 CancelAsync 메소드를 호출하면 예외가 발생하므로 WorkerSupportsCancellation 프로퍼티값을 검사하는 방어적 코딩을 해준다.

if (threadWaitPermit.WorkerSupportsCancellation)
    threadWaitPermit.CancelAsync();

3. BackgroundWorker 스레드 작업을 수행하는 DoWork 이벤트의 이벤트 핸들러에서 CancellationPending 프로퍼티값을 검사한다. CancelAsync() 메소드를 호출하면 CancellationPending 프로퍼티 값이 true 가 된다.

private void threadWaitPermit_DoWork(object sender, DoWorkEventArgs e)
{
    ...
    if (threadWaitPermit.CancellationPending)
        ....



BackgroundWorker 스레드의 작업과정 갱신

보통 스레드의 작업 처리 과정을 메인 스레드(UI스레드)의 프로그레스바를 그려주는 것으로 표시한다. BackgroundWorker 스레드에서 이러한 일반적인 처리 과정을 돕는다.

1. WorkerReportsProgress 프로퍼티값을 true 로 설정한다. 스레드에서 작업 갱신 이벤트(ProgressChanged) 발생시 호출될 이벤트 핸들러를 등록한다.

threadWaitPermit.WorkerReportsProgress = true;
threadWaitPermit.ProgressChanged += ProgressChangedEventHandler(threadWaitPermit_ProgressChanged);

2. BackgroundWorker 스레드에서 UI 작업을 위해 ReportProgress() 메소드를 호출한다. ProgressChanged 이벤트에 등록된 이벤트 핸들러가 호출된다. ReportProgress 메소드의 매개변수로 사용할 정보값을 넘겨준다.

private void threadWaitPermit_DoWork(object sender, DoWorkEventArgs e)
{
    ...
    BackgroundWorker worker = sender as BackgoundWorker;
    worker.ReportProgress(10);
    ...

3. ProgressChanged 이벤트에 등록한 이벤트 핸들러를 구현해준다. 이 이벤트 핸들러에서만 UI 스레드에 접근하여 값을(보통 컨트롤) 변경시켜줄 수 있다. ProgressChangedEventArgs 매개변수에서 ProgressPercentage 프로퍼티값이 ReportProgress 메소드에서 전달한 매개변수값이다.

private void threadWaitPermit_DoWork(object sender, ProgressChangedEventArgs e)
{
    ...
    progress1.Value = e.ProgressPercentage;
    ...


※ ReportProgress 메소드는 2개의 메소드로 오버로딩되어 있다.

두번째 매개변수의 타입이 Object 타입이므로 사용자가 정의한 특정 정보를 담은 데이터를 넘겨줄 수 있다.

ProgressChangedEventArgs 타입의 멤버를 보면 UserState 프로퍼티를 이용하여 두번째 매개변수로 넘긴 값을 받을 수 있다.




BackgroundWorker 스레드의 종료

BackgroundWorker 스레드 종료 이벤트는 RunWorkerCompleted 이다. 이 이벤트에 이벤트 핸들러를 연결 시키면 스레드 종료 시 연결시킨 이벤트 핸들러가 호출된다. 아래 코드에서는 threadWaitPermit_RunWorkerCompleted 이벤트 핸들러를 연결 시켰다.

threadWaitPermit.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(threadWaitPermit_RunWorkerCompleted);

BackgroundWorker 스레드에서 스레드가 종료되는 상황(취소, 완료, 예외)에 따른 처리를 RunWorkerCompleted 이벤트 핸들러에서 처리해주면 된다.

private void threadWaitPermit_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // 스레드 작업에서 예외가 발생하여 종료된 경우
    if (e.Error != null)
        ...
 
    // 스레드 작업에서 취소 명령이 발생한 경우
    // 스레드에서 e.Cancelled 프로퍼티에 값을 설정해 줄 수 있다.
    if (e.Cancelled)
        ...


Posted by six605
,