728x90
1. 강의 개요
이번 강의에서는 Windows Forms 애플리케이션에서 **크로스 스레드 작업(Cross-Thread Operation)**과 InvokeRequired를 활용해 UI 업데이트를 안전하게 처리하는 방법을 학습합니다.
Windows Forms에서는 UI 컨트롤을 메인 스레드(UI 스레드)에서만 업데이트할 수 있으므로, 백그라운드 작업 스레드에서 UI를 안전하게 업데이트하려면 추가적인 조치가 필요합니다.
2. 학습 목표
- 멀티스레딩 환경에서 UI 컨트롤을 안전하게 업데이트.
- InvokeRequired와 Invoke() 메서드를 사용해 크로스 스레드 작업 처리.
- 백그라운드 작업 중 ProgressBar와 Label 업데이트 구현.
3. 크로스 스레드 작업이란?
크로스 스레드 작업은 메인 스레드(UI 스레드) 외의 다른 스레드에서 UI 컨트롤을 직접 수정하려고 할 때 발생합니다.
- Windows Forms에서는 **스레드 간격 위반(Thread Affinity Violation)**을 방지하기 위해 UI 컨트롤을 직접 업데이트할 수 없습니다.
- 이 문제를 해결하려면 UI 작업은 항상 메인 스레드에서 실행해야 합니다.
InvokeRequired와 Invoke 메서드
개념 설명 예제
InvokeRequired | 현재 스레드가 UI 스레드인지 확인 | if (control.InvokeRequired) {...} |
Invoke() | UI 스레드에서 작업 실행 | control.Invoke(new Action(() => {...})); |
4. 실습: 백그라운드 작업 중 UI 업데이트
요구사항
- Thread 또는 Task로 긴 작업을 백그라운드에서 실행.
- 작업 진행 상황을 ProgressBar와 Label로 표시.
- Invoke를 사용해 UI를 안전하게 업데이트.
폼 구성
컨트롤 타입 이름 텍스트 위치 크기
ProgressBar | progressBar1 | (없음) | 상단 | (400 x 30) |
Label | lblStatus | "상태: 대기 중" | 중간 | (400 x 30) |
Button | btnStartWork | "작업 시작" | 하단 | (150 x 30) |
코드 작성
Form1.cs
using System;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
// 작업 시작 버튼 클릭 이벤트
private void BtnStartWork_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
lblStatus.Text = "상태: 작업 진행 중...";
// Task를 사용한 비동기 작업
Task.Run(() =>
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50); // 50ms 지연
UpdateUI(i); // UI 업데이트
}
UpdateStatus("상태: 작업 완료");
});
}
// UI 업데이트 (스레드 안전)
private void UpdateUI(int progress)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new Action(() =>
{
progressBar1.Value = progress;
}));
}
else
{
progressBar1.Value = progress;
}
}
// 상태 업데이트 (스레드 안전)
private void UpdateStatus(string status)
{
if (lblStatus.InvokeRequired)
{
lblStatus.Invoke(new Action(() =>
{
lblStatus.Text = status;
}));
}
else
{
lblStatus.Text = status;
}
}
}
}
Form1.Designer.cs
namespace WindowsFormsApp1
{
partial class Form1
{
private System.ComponentModel.IContainer components = null;
private ProgressBar progressBar1;
private Label lblStatus;
private Button btnStartWork;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.progressBar1 = new ProgressBar();
this.lblStatus = new Label();
this.btnStartWork = 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(400, 30);
this.progressBar1.TabIndex = 0;
// lblStatus
this.lblStatus.AutoSize = true;
this.lblStatus.Location = new System.Drawing.Point(20, 60);
this.lblStatus.Name = "lblStatus";
this.lblStatus.Size = new System.Drawing.Size(120, 20);
this.lblStatus.TabIndex = 1;
this.lblStatus.Text = "상태: 대기 중";
// btnStartWork
this.btnStartWork.Location = new System.Drawing.Point(20, 100);
this.btnStartWork.Name = "btnStartWork";
this.btnStartWork.Size = new System.Drawing.Size(150, 30);
this.btnStartWork.TabIndex = 2;
this.btnStartWork.Text = "작업 시작";
this.btnStartWork.UseVisualStyleBackColor = true;
this.btnStartWork.Click += new System.EventHandler(this.BtnStartWork_Click);
// Form1
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(450, 200);
this.Controls.Add(this.btnStartWork);
this.Controls.Add(this.lblStatus);
this.Controls.Add(this.progressBar1);
this.Name = "Form1";
this.Text = "크로스 스레드 작업 예제";
this.ResumeLayout(false);
this.PerformLayout();
}
}
}
5. 실행 결과
- 작업 시작
- "작업 시작" 버튼 클릭 시 ProgressBar가 점진적으로 업데이트됩니다.
- Label에는 작업 진행 상태가 표시됩니다.
- UI 멈춤 없이 업데이트
- 작업이 진행되는 동안 버튼을 클릭하거나 폼을 이동해도 UI는 멈추지 않습니다.
- 작업 완료
- ProgressBar가 100%에 도달하면 Label 상태가 "작업 완료"로 변경됩니다.
6. 주요 개념 요약
- 크로스 스레드 작업 방지
- UI 컨트롤은 메인(UI) 스레드에서만 업데이트할 수 있습니다.
- InvokeRequired
- 현재 스레드가 UI 스레드인지 확인하고, 필요한 경우 Invoke를 사용해 작업을 메인 스레드에서 실행합니다.
- Task와 UI 업데이트
- Task 또는 Thread에서 백그라운드 작업을 실행하며, UI 업데이트는 반드시 Invoke로 처리합니다.
728x90
'Study > C#' 카테고리의 다른 글
C# Windows Forms 강의 27편: Chart 컨트롤을 사용한 데이터 시각화 (0) | 2025.03.02 |
---|---|
C# Windows Forms 강의 26편: BackgroundWorker로 비동기 작업 처리 (0) | 2025.03.01 |
C# Windows Forms 강의 24편: NotifyIcon을 활용한 시스템 트레이 아이콘 (0) | 2025.02.27 |
C# Windows Forms 강의 23편: 사용자 설정(Configuration) 저장 및 로드 (0) | 2025.02.26 |
C# Windows Forms 강의 22편: 사용자 입력 검증(Input Validation) (0) | 2025.02.25 |