C#/Advanced C#

[C# Thread] BackgroundWorker 클래스

로파이 2021. 12. 15. 23:00

System.ComponentModel.BackgroundWorker 클래스

백그라운드에서 동작하는 스레드를 사용하는 스레드 풀을 사용하여 작업을 실행할 수 있는 클래스

 

내부적으로 System.Threading.Thread.QueueUserWorkItem() 함수를 이용하여 작업을 실행한다.

 

백그라운드 스레드가 예외를 던지면 전체 어플리케이션이 종료할 수 있다. BackgroundWorker 는 백그라운드에서 실행되는 작업에서 발생하는 취소, 예외나 진행상황을 포그라운드(Foreground) 스레드에 전달할 수 있다. 따라서 스레드간 커뮤니케이션을 가능하게한다.

 

작업의 실행

DoWork 속성에 실행할 작업의 대리자를 등록한다.

RunWorkerAsync()는 등록된 작업을 실행한다. System.Threading.Thread.QueueUserWorkItem() 함수를 이용하여 실행한다.

 

오류 발생

작업에서 오류가 발생하면 RunWorkerCompleted 이벤트 매개변수 RunWorkerCompletedEventArgs 인수의 Error 속성에 전달하여 작업 완료시 호출되는 대리자 함수 내에서 Error 속성을 확인하여 알아낼 수 있다.

 

작업의 취소

백그라운드워커 객체는 CancelAsync()로 CancellationPending 속성을 true로 만든다.

작업의 실행 해당 속성을 보고 취소 상황을 알 수 있으며 DoWorkEventArgs의 Cancel 속성을 true로 만들면 RunWorkerCompletedEventArgs의 속성이 Cancelled 된 것을 알 수 있다.

워커 객체의 WorkerSupportsCancellation 속성이 true로 설정되어야 지원된다.

 

작업 진행상황 보고

WorkerReportsProgress가 true 이어야하며 ReportProgress 함수를 호출해서 알린다.

ProgressChanged 대리자를 등록하면 위 함수가 호출될 때마다 대리자가 호출된다.

 

코드 예시

public static void UseBackgroundWorker()
{
    using(ManualResetEvent resetEvent = new ManualResetEvent(false))
    {
        Random r = new Random();

        BackgroundWorker backgroundWorkerExample = new BackgroundWorker();
        backgroundWorkerExample.WorkerReportsProgress = true;
        backgroundWorkerExample.WorkerSupportsCancellation = true;
        backgroundWorkerExample.DoWork += (sender, doWorkEventArgs) =>
        {
            BackgroundWorker worker = sender as BackgroundWorker;

            try
            {
                for (int i = 0; i < 100; ++i)
                {
                    int s = r.Next(0, 1000);

                    if (s < 5)
                    {
                        throw new InvalidOperationException("Abort Work");
                    }

                    if (i % 10 == 0)
                    {
                        worker.ReportProgress(i);
                    }
                }
            }
            catch (Exception e)
            {
                doWorkEventArgs.Cancel = true;
                throw;
            }
            finally
            {
                resetEvent.Set();
            }
        };

        backgroundWorkerExample.ProgressChanged += (sender, e) =>
        {
            Console.WriteLine($"Progress {e.ProgressPercentage}...");
        };

        backgroundWorkerExample.RunWorkerCompleted += (sender, e) =>
        {
            if (e.Error != null)
            {
                Console.WriteLine($"Job is Cancelled {e.Error.Message}");
            }
            else
            {
                Console.WriteLine($"Job is done");
            }
        };

        backgroundWorkerExample.RunWorkerAsync();

        resetEvent.WaitOne();
    }
}