728x90
1. 강의 개요
이번 강의에서는 Windows Forms에서 멀티스레딩을 구현하기 위한 Thread와 Task를 학습합니다.
멀티스레딩은 긴 작업을 백그라운드에서 수행하고, UI가 멈추지 않도록 해줍니다.
2. 학습 목표
- Thread 클래스를 사용해 기본적인 멀티스레딩 구현.
- Task 클래스를 활용해 비동기 작업 처리.
- 멀티스레딩 작업 중 UI 업데이트 안전하게 처리.
3. Thread와 Task
Thread란?
Thread는 프로그램 내에서 작업을 동시에 실행할 수 있도록 하는 기본 단위입니다.
- UI 스레드와 독립적으로 동작.
- Thread.Start()로 실행.
Task란?
Task는 비동기 작업을 수행하는 더 높은 수준의 추상화입니다.
- 작업을 정의하고, 백그라운드에서 실행.
- Task.Run()으로 실행.
4. 멀티스레딩에서의 주의점
- UI 업데이트는 UI 스레드에서만 가능
- UI 컨트롤을 업데이트하려면 Invoke 또는 BeginInvoke를 사용해야 합니다.
- 스레드 충돌 방지
- 여러 스레드가 같은 데이터에 접근하지 않도록 조치해야 합니다.
5. 실습: Thread와 Task를 사용한 멀티스레딩
요구사항
- ProgressBar와 Label로 작업 진행 상태 표시.
- 버튼 클릭 시 Thread 또는 Task를 사용해 백그라운드 작업 실행.
- 작업 진행 중에도 UI가 멈추지 않도록 구현.
폼 구성
컨트롤 타입 이름 텍스트 위치 크기
ProgressBar | progressBar1 | (없음) | 상단 | (400 x 30) |
Label | lblStatus | "상태: 대기 중" | 중간 | (400 x 30) |
Button | btnStartThread | "Thread로 작업 시작" | 하단 왼쪽 | (150 x 30) |
Button | btnStartTask | "Task로 작업 시작" | 하단 오른쪽 | (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();
}
// Thread를 사용한 작업
private void BtnStartThread_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
lblStatus.Text = "상태: Thread 작업 중...";
Thread thread = new Thread(() =>
{
for (int i = 1; i <= 100; i++)
{
Thread.Sleep(50); // 50ms 지연
UpdateUI(i); // UI 업데이트
}
UpdateUIStatus("상태: Thread 작업 완료");
});
thread.Start();
}
// Task를 사용한 작업
private void BtnStartTask_Click(object sender, EventArgs e)
{
progressBar1.Value = 0;
lblStatus.Text = "상태: Task 작업 중...";
Task.Run(async () =>
{
for (int i = 1; i <= 100; i++)
{
await Task.Delay(50); // 50ms 지연
UpdateUI(i); // UI 업데이트
}
UpdateUIStatus("상태: Task 작업 완료");
});
}
// UI 업데이트 메서드 (스레드 안전)
private void UpdateUI(int progress)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke(new Action(() =>
{
progressBar1.Value = progress;
}));
}
else
{
progressBar1.Value = progress;
}
}
private void UpdateUIStatus(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 btnStartThread;
private Button btnStartTask;
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.btnStartThread = new Button();
this.btnStartTask = 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 = "상태: 대기 중";
// btnStartThread
this.btnStartThread.Location = new System.Drawing.Point(20, 100);
this.btnStartThread.Name = "btnStartThread";
this.btnStartThread.Size = new System.Drawing.Size(150, 30);
this.btnStartThread.TabIndex = 2;
this.btnStartThread.Text = "Thread로 작업 시작";
this.btnStartThread.UseVisualStyleBackColor = true;
this.btnStartThread.Click += new System.EventHandler(this.BtnStartThread_Click);
// btnStartTask
this.btnStartTask.Location = new System.Drawing.Point(200, 100);
this.btnStartTask.Name = "btnStartTask";
this.btnStartTask.Size = new System.Drawing.Size(150, 30);
this.btnStartTask.TabIndex = 3;
this.btnStartTask.Text = "Task로 작업 시작";
this.btnStartTask.UseVisualStyleBackColor = true;
this.btnStartTask.Click += new System.EventHandler(this.BtnStartTask_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.btnStartTask);
this.Controls.Add(this.btnStartThread);
this.Controls.Add(this.lblStatus);
this.Controls.Add(this.progressBar1);
this.Name = "Form1";
this.Text = "멀티스레딩 예제";
this.ResumeLayout(false);
this.PerformLayout();
}
}
}
6. 실행 결과
- Thread 작업
- "Thread로 작업 시작" 버튼 클릭 시, ProgressBar가 0에서 100까지 점진적으로 증가합니다.
- 작업 중에도 UI는 반응하며, 작업 완료 후 상태가 업데이트됩니다.
- Task 작업
- "Task로 작업 시작" 버튼 클릭 시, ProgressBar가 동일한 방식으로 업데이트됩니다.
- 작업 완료 후 상태 메시지가 변경됩니다.
7. 주요 개념 요약
- Thread는 작업을 별도 스레드에서 실행하여 UI의 응답성을 유지.
- Task는 더 높은 수준의 비동기 작업 추상화를 제공하며, 비슷한 작업에 적합.
- UI 업데이트는 반드시 Invoke 또는 BeginInvoke를 사용해 스레드 안전하게 처리해야 함.
728x90
'Study > C#' 카테고리의 다른 글
C# Windows Forms 강의 22편: 사용자 입력 검증(Input Validation) (0) | 2025.02.25 |
---|---|
C# Windows Forms 강의 21편: LINQ를 활용한 데이터 검색과 필터링 (0) | 2025.02.24 |
C# Windows Forms 강의 19편: 데이터 바인딩(Data Binding)과 BindingSource 사용 (0) | 2025.02.22 |
C# Windows Forms 강의 18편: Process 클래스와 외부 프로그램 실행 (0) | 2025.02.21 |
C# Windows Forms 강의 17편: 파일 입출력(File I/O)과 OpenFileDialog, SaveFileDialog (0) | 2025.02.20 |