본문 바로가기
📁 [4] 개발자 정보 & 코드 노트/C#

C# Windows Forms 강의 46편: BackgroundWorker로 비동기 작업 처리

by wawManager 2025. 3. 21.

1. 강의 개요

이번 강의에서는 BackgroundWorker를 사용하여 긴 작업을 수행하는 동안 UI가 멈추지 않도록 비동기 작업을 처리하는 방법을 학습합니다.
이 강의에서는 파일 복사 시뮬레이션 예제를 통해 작업 진행률을 ProgressBar로 표시하며, 작업 완료 시 메시지를 표시하는 애플리케이션을 제작합니다.


2. 학습 목표

  1. BackgroundWorker의 기본 사용법 학습.
  2. 작업 진행률을 ProgressBar로 표시.
  3. 비동기 작업 완료 시 UI 업데이트.

3. BackgroundWorker란?

BackgroundWorker는 긴 작업을 비동기로 실행하면서, UI 스레드를 차단하지 않고 작업 상태를 업데이트할 수 있도록 설계된 컴포넌트입니다.

BackgroundWorker 주요 이벤트

이벤트 설명

DoWork 백그라운드 작업 수행.
ProgressChanged 작업 진행률 변경 시 호출.
RunWorkerCompleted 작업 완료 시 호출.

BackgroundWorker 주요 속성

속성 설명

WorkerReportsProgress 진행률 보고를 활성화.
WorkerSupportsCancellation 작업 취소 지원 여부.

4. 실습: 파일 복사 시뮬레이션

요구사항

  1. BackgroundWorker를 사용해 비동기 작업(파일 복사 시뮬레이션)을 구현.
  2. ProgressBar를 사용해 작업 진행률 표시.
  3. 버튼으로 작업 시작 및 취소.

폼 구성

컨트롤 타입 이름 텍스트 위치 크기

ProgressBar progressBar1 (없음) 중앙 (300 x 30)
Button btnStart "작업 시작" 하단 왼쪽 (100 x 30)
Button btnCancel "작업 취소" 하단 오른쪽 (100 x 30)

코드 작성

Form1.cs

using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        private BackgroundWorker backgroundWorker;

        public Form1()
        {
            InitializeComponent();
            InitializeBackgroundWorker();
        }

        private void InitializeBackgroundWorker()
        {
            // BackgroundWorker 초기화
            backgroundWorker = new BackgroundWorker
            {
                WorkerReportsProgress = true, // 진행률 보고 활성화
                WorkerSupportsCancellation = true // 작업 취소 지원
            };

            // 이벤트 연결
            backgroundWorker.DoWork += BackgroundWorker_DoWork;
            backgroundWorker.ProgressChanged += BackgroundWorker_ProgressChanged;
            backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;

            // 버튼 이벤트 연결
            btnStart.Click += BtnStart_Click;
            btnCancel.Click += BtnCancel_Click;
        }

        // "작업 시작" 버튼 클릭 이벤트
        private void BtnStart_Click(object sender, EventArgs e)
        {
            if (!backgroundWorker.IsBusy)
            {
                progressBar1.Value = 0; // 진행률 초기화
                backgroundWorker.RunWorkerAsync(); // 작업 시작
            }
        }

        // "작업 취소" 버튼 클릭 이벤트
        private void BtnCancel_Click(object sender, EventArgs e)
        {
            if (backgroundWorker.IsBusy)
            {
                backgroundWorker.CancelAsync(); // 작업 취소 요청
            }
        }

        // BackgroundWorker DoWork 이벤트 (작업 실행)
        private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            for (int i = 1; i <= 100; i++)
            {
                if (backgroundWorker.CancellationPending) // 작업 취소 확인
                {
                    e.Cancel = true; // 작업 취소 표시
                    return;
                }

                Thread.Sleep(50); // 작업 시뮬레이션 (0.05초 대기)
                backgroundWorker.ReportProgress(i); // 진행률 보고
            }
        }

        // BackgroundWorker ProgressChanged 이벤트 (진행률 업데이트)
        private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar1.Value = e.ProgressPercentage; // ProgressBar 업데이트
        }

        // BackgroundWorker RunWorkerCompleted 이벤트 (작업 완료)
        private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                MessageBox.Show("작업이 취소되었습니다.", "알림");
            }
            else
            {
                MessageBox.Show("작업이 완료되었습니다!", "완료");
            }
        }
    }
}

Form1.Designer.cs

namespace WindowsFormsApp1
{
    partial class Form1
    {
        private System.ComponentModel.IContainer components = null;
        private ProgressBar progressBar1;
        private Button btnStart;
        private Button btnCancel;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.progressBar1 = new ProgressBar();
            this.btnStart = new Button();
            this.btnCancel = new Button();
            this.SuspendLayout();

            // progressBar1
            this.progressBar1.Location = new System.Drawing.Point(20, 20);
            this.progressBar1.Name = "progressBar1";
            this.progressBar1.Size = new System.Drawing.Size(300, 30);
            this.progressBar1.TabIndex = 0;

            // btnStart
            this.btnStart.Location = new System.Drawing.Point(20, 70);
            this.btnStart.Name = "btnStart";
            this.btnStart.Size = new System.Drawing.Size(100, 30);
            this.btnStart.Text = "작업 시작";
            this.btnStart.UseVisualStyleBackColor = true;

            // btnCancel
            this.btnCancel.Location = new System.Drawing.Point(220, 70);
            this.btnCancel.Name = "btnCancel";
            this.btnCancel.Size = new System.Drawing.Size(100, 30);
            this.btnCancel.Text = "작업 취소";
            this.btnCancel.UseVisualStyleBackColor = true;

            // Form1
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(350, 120);
            this.Controls.Add(this.btnCancel);
            this.Controls.Add(this.btnStart);
            this.Controls.Add(this.progressBar1);
            this.Name = "Form1";
            this.Text = "비동기 작업 예제";
            this.ResumeLayout(false);
        }
    }
}

5. 실행 결과

  1. 초기 상태
    • ProgressBar는 비어 있고, "작업 시작"과 "작업 취소" 버튼이 표시됩니다.
  2. 작업 시작
    • "작업 시작" 버튼 클릭 → ProgressBar가 점차 채워지고, 작업이 실행됩니다.
    • 작업 중에도 UI는 응답성을 유지합니다.
  3. 작업 완료
    • ProgressBar가 끝까지 채워지면 "작업이 완료되었습니다!" 메시지가 표시됩니다.
  4. 작업 취소
    • "작업 취소" 버튼 클릭 → 작업이 중단되고 "작업이 취소되었습니다." 메시지가 표시됩니다.

6. 주요 개념 요약

  1. BackgroundWorker를 통한 비동기 작업
    • 긴 작업을 UI 스레드와 분리해 실행하며, UI의 응답성을 유지.
  2. 작업 상태 업데이트
    • ReportProgress와 ProgressChanged 이벤트를 사용해 작업 진행률 표시.
  3. 작업 취소 처리
    • CancellationPending 속성을 확인해 작업 취소 로직 구현.