■ 멀티쓰레드(Multi Thread)란?
여러개의 쓰레드가 동시에 실행하는 것
WPF가 실행되면 Rendering Thread, UI Thread가 생성됩니다.
Rendering Thread는 백그라운드에서 실행되며 WPF화면을 Rendering하는데 사용됩니다.
UI Thread는 사용자의 입력, 이벤트 처리, 화면 그리기 등에 사용됩니다.
WPF는 STA(Single Thread Apartment) 모델
■ BackgroundWorker
- BackgroundWorker 클래스는 UI Thread 외에 시간이 많이 걸리는 작업을 비동기 적으로 작업하기 위한 클래스입니다.
- BackgroundWorker는 DoWork, ProgressChanged, RunWorkerCompleted 이벤트를 통해 작업 내용을 구현합니다.
· DoWork : 실제 작업할 내용
· ProgressChanged : 진행 사항 전달
· RunWorkerCompleted : DoWork에 구현된 작업이 완료 된 후 실행할 작업
■ Dispatcher
- Dispatcher는 UI Thread 내의 작업 항목 큐를 우선 순위에 따라 유지 관리합니다.
- Background Thread는 UI Thread에 연결된 객체의 값을 변경하거나 조작하기 위해서는 Dispatcher를 사용해야합니다.
- Dispatcher를 사용하지 않고 UI 객체를 조작하게 되면 "System.InvalidOperationException"이 발생합니다.
■ Multi Thread Example (BackgroundWorker, Dispatcher)
- Example 설명
· 1부터 입력한 숫자만큼 1씩 증가
· 증가하면서 listbox, progressbar, label에 진행사항 표기
- MainWindow.xaml
<Window x:Class="MultiThread.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MultiThread"
mc:Ignorable="d"
Title="MainWindow" Height="500" Width="500">
<StackPanel>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Label Content="숫자 입력 : " Margin="10,10,10,10"/>
<TextBox x:Name="txtBox" Width="100" Margin="10,10,10,10"/>
<Button x:Name="startBtn" Content="시작" Width="100" Margin="10,10,10,10" Click="startBtn_Click"/>
<Button x:Name="stopBtn" Content="정지" Width="100" Margin="10,10,10,10" Click="stopBtn_Click"/>
</StackPanel>
<ProgressBar x:Name="progressBar" Height="20" Margin="10,10,10,10"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Label x:Name="lblNum" Content="0 %" Margin="10,10,10,10"/>
</StackPanel>
<ListBox x:Name="lstBox" Width="400" Height="300" HorizontalAlignment="Center"/>
</StackPanel>
</Window>
- MainWindow.xaml.cs
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
namespace MultiThread
{
public partial class MainWindow : Window
{
private BackgroundWorker bgThread;
public MainWindow()
{
InitializeComponent();
}
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
bgThread = new BackgroundWorker()
{
WorkerReportsProgress = true, // ProgressChanged 이벤트 발생 여부(작업 진행율)
WorkerSupportsCancellation = true // 작업 취소 가능 여부
};
bgThread.DoWork += Thread_DoWork; // 작업 method 정의
bgThread.ProgressChanged += Thread_ProgressChanged;
// UI 진행사항, WorkerReportsProgress가 true일 때만 발생
bgThread.RunWorkerCompleted += Thread_RunWorkerCompleted; // 작업 완료되었을 때 callback method 정의
}
// background worker 실행할 작업
// DoWork 이벤트 메소드에서 UI에 직접 접근하게 되면 "InvalidOperationException" 오류 발생
private void Thread_DoWork(object sender, DoWorkEventArgs e)
{
int count = (int)e.Argument;
for(int i = 1; i <= count; i++)
{
if (bgThread.CancellationPending)
{
e.Cancel = true;
return;
}
else
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate ()
{
e.Result = count;
int per = (int)Math.Round((double)(100 * i) / count);
lblNum.Content = per + " %";
lstBox.Items.Add("진행중 : " + i);
lstBox.Items.MoveCurrentToLast();
lstBox.ScrollIntoView(lstBox.Items.CurrentItem);
});
bgThread.ReportProgress(i);
Thread.Sleep(100);
}
}
}
// 작업 진행률
private void Thread_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
// 작업 완료
private void Thread_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled) MessageBox.Show("작업 취소");
else if (e.Error != null) MessageBox.Show("ERROR 발생" + e.Error);
else
{
progressBar.Value = (int)e.Result;
lstBox.Items.Add("작업 완료");
lstBox.Items.MoveCurrentToLast();
lstBox.ScrollIntoView(lstBox.Items.CurrentItem);
MessageBox.Show("작업 완료");
}
}
private void startBtn_Click(object sender, RoutedEventArgs e)
{
int num;
if(!int.TryParse(txtBox.Text, out num))
{
MessageBox.Show("숫자를 입력하세요.");
return;
}
progressBar.Maximum = num;
lstBox.Items.Clear();
bgThread.RunWorkerAsync(num);
}
private void stopBtn_Click(object sender, RoutedEventArgs e)
{
bgThread.CancelAsync();
}
}
}
- 숫자 30 입력, 진행 완료 전에 정지
- 숫자 20 입력, 진행 정상 완료
'개인공부 > WPF' 카테고리의 다른 글
[WPF] DataContext (0) | 2022.07.23 |
---|---|
[WPF] 이벤트 라우팅, 터널링(Tunneling), 버블링(Bubbling) (0) | 2022.05.29 |
[WPF] 의존 속성(DependencyProperty) (0) | 2022.05.20 |
[WPF] 데이터 바인딩(Data Binding) (0) | 2022.04.26 |
[WPF] Hello World (0) | 2022.04.19 |