BackgroundWorker 클래스 사용

WinForm을 이용한 프로그램을 작업할 때 종종 오랜시간 반복적인 작업을 처리하여야 하는 경우가 있습니다. 진행되는 상태를 표시해 주고 일시 중지 혹은 작업이 마무리 되었을 때 사용자에게 알림 메시지를 보여주는 형식으로 작업을 하게 됩니다. 여기서 문제가 반복문안에 UI 를 갱신하는 코드를 넣으면 의도한 대로 동작하지 않게 됩니다. 특히 버튼 이벤트는 반응을 하지 않게되고 작업이 끝난 후 버튼이 눌려지는 듯한 현상을 보이게 됩니다. 이럴 때 BackgroundWorker 클래스 를 사용하여 작업하면 의도한 대로 버튼 이벤트가 발생되도록 할 수 있습니다. 이 클래스는 .Net 2.0 부터 추가되었고 자세한 설명은 다음의 링크에서 확인할 수 있습니다.

MSDN 설명 보기

사용자가 설정한 키워드 목록을 OpenAPI 를 이용하여 검색 결과를 가져오는 간단한 예제를 구성해 보겠습니다. 먼저 Form 을 아래와 같이 디자인 합니다. 처리되는 키워드와 진행 상황을 표시해 줄 Text, Label 컨트롤을 추가합니다.

폼 디자인

폼 디자인

BackgroundWorker 클래스 변수를 선언합니다. 그리고 폼이 초기화될 때 BackgroundWorker 클래스가 동작할 사항을 정의해 줍니다.

private BackgroundWorker bwWrite;

bwWrite = new BackgroundWorker();
bwWrite.WorkerSupportsCancellation = true;
bwWrite.DoWork += new DoWorkEventHandler(doWrite);
bwWrite.RunWorkerCompleted += new RunWorkerCompletedEventHandler(write_RunWorkerCompleted);

5번째 행에 doWrite 라는 메소드를 실행하도록 설정하고 6번째 행에 작업이 종료되면 write_RunWorkerCompleted 메소드가 실행되도록 설정하였습니다. doWrite 메소드에서는 시간이 오래 걸리는 루프 등의 루틴을 추가하면 됩니다. OpenAPI 를 통하여 검색결과를 가져오는 부분이 될 것입니다. write_RunWorkerCompleted 메소드에서는 doWrite 메소드의 호출이 완료되었을 때 원하는 동작을 하도록 작업하면 됩니다.

각 버튼 이벤트를 다음과 같이 정의합니다.

private void btnStart_Click(object sender, EventArgs e)
{
	btnStart.Enabled = false;
	btnStop.Enabled = true;

	bwWrite.RunWorkerAsync();
}

private void btnStop_Click(object sender, EventArgs e)
{
	bwWrite.CancelAsync();
	btnStart.Enabled = true;
	btnStop.Enabled = false;
}

6번째 행에서 이전에 정의한 대로 doWrite 메소드를 실행하도록 RunWorkerAsync 메소드를 호출합니다. 중지 버튼을 클릭하면 CancelAsync 메소드를 호출하여 취소되도록 합니다. 여기서 유의해야 할 것은 CancelAsync 메소드가 호출되었다고 해서 바로 종료되는 것이 아니라는 것 입니다. doWrite메소드 내에서 다음과 같은 방식으로  취소여부를 판단하여 종료하여야 합니다.

private void doWrite(object sender, DoWorkEventArgs e)
{
.
.
.
	for (var i = keywordIndex; i < keyword.Length; i++)
	{
		if (bwWrite.CancellationPending == true)
		{
			e.Cancel = true;
			return;
		}
		.
		.
		.
	}
}

작업진행 사항을 Text, Label의 속성을 변경시키는 방법으로 doWrite 메소드내에 코드를 작성하면 “자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다” 라는 오류가 발생합니다. 메시지 내용대로 별도의 스레드에서 폼에 생성된 컨트롤에 접근하려고 하여 발생되는 오류 입니다. 이럴때는 다음과 같이 Invoke 메소드를 호출하는 방식으로 처리합니다. 이렇게 하면 오류가 발생하지 않고 의도한 대로 진행상황을 Label 컨트롤에 나타낼 수 있습니다.

delegate void delegateSetProgressLabel(string text);

private void setProgressLabel(string text)
{
	if (lblProgressStatus.InvokeRequired)
	{
		var delegateCall = new delegateSetProgressLabel(setProgressLabel);

		this.Invoke(delegateCall, text);
	}
	else
	{
		lblProgressStatus.Text = text;
	}
}

간략하게  BackgroundWorker 클래스 사용 방법에 대하여 알아보았습니다.

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.

Time limit is exhausted. Please reload the CAPTCHA.