n 멀티쓰레드란 여러 개의 쓰레드가 동시에 특정 코드블럭을 실행하는 것이다.
n 멀티쓰레드는 모든 부분에서 사용가능 하지만 채팅 프로그램처럼 내가 글을 쓰는 동안에 상대방이 글을 보내면 빠르게 반응해서 UI 화면에 그려야 하는 경우등에 주로 사용된다.
n 모든 WPF 프로그램은 최소한의 렌더링을 위한 백그라운드 쓰레드와 UI 스레드(UI 인터페이스 관리) 두개의 쓰레드로 기동된다. UI 스레드는 사용자 입력을 받고 화면을 그리고, 코드를 실행하고, 이벤트등을 처리한다.
n WPF는 기본적으로 STA(Single Thread Apartment) 모델을 지원하는데 하나의 쓰레드는 전체 응용프로그램에서 실행되고 모든 WPF 객체를 소유하고 있고 TextBox같은 WPF UIElements 요소들은 쓰레드 선호도라는 것이 있어 다른 쓰레드와 상호작용 할 수 없다. (UI 컨트롤은 다른 쓰레드에서 업데이트 할 수가 없다.)
n 즉 화면을 그리는 쓰레드는 컨트롤들을 소유하고 다른 쓰레드에서는 직접 접근할 수 없도록 되어 있다. 이를 쓰레드 선호도라 한다.
WPF에서 멀티 쓰레드 처리를 위해서 Dispatcher를 이용할 수도 있고 Background Worker를 사용할 수도 있다.
1.5.1 Dispatcher를 이용한 WPF 멀티쓰레드 프로그래밍
n 모든 비주얼 객체(TextBox, Button등)는 DispatcherObject 클래스를 상속받고 있으며 DispatcherObject가 UI 쓰레드의 Dispatcher 를 얻을 수 있게 해준다. 결국 이 Dispatcher 때문에 다른 쓰레드에서 UI 컨트롤을 변경 할 수 없는 것이다.
n TextBox 같은 컨트롤을 다른 쓰레드에서 수정하려면 Dispatcher를 통해서 해야 된다는 것이다.
n Dispatcher는 System.Windows.Threading.Dispatcher 클래스의 인스턴스로 응용 프로그램 쓰레드를 소유(쓰레드에 속한 모든 개체를 소유)하고 작업 항목의 대기열을 관리하는데 각각의 우선 순위를 사용하여 FIFO (First In First Out) 방식으로 UI 작업을 실행한다. 새 스레드를 만들지 않는다. 즉 멀티 스레드가 아니다.
n DispatcherObject 클래스의 멤버로는 다음과 같은 것이 있다.
[속성]
Dispatcher : Dispatcher를 가지고 온다.
[메소드]
CheckAccess() : 코드가 객체를 사용할 수있는 스레드이면 true를 반환.
VerifyAccess() : 코드가 객체를 사용하기 위해 올바른 스레드인지 판단. 가능하다면 아무 작업도 수행하지 않고 그렇지 않으면 "InvalidOperationException"을 발생시킴.
ð WPF 응용프로그램에서 수시로 자주 호출한다.
GetType () : 현재 인스턴스의 유형을 가져온다.
n 하나의 쓰레드에서 UIElement를 만들면 Dispatcher도 만들어지는데 새로운 TextBox를 만들면 거기에 따라 Dispatcher 객체도 만들어 진다.
n 대부분의 UI 객체(TextBox등)는 DisplatcherObject에서 파생되는데 이것은 Dispatcher와 연결된 객체라고 보면 된다.
n 만약 잘못된 쓰레드가 UI객체에 접근하려고 하면 DispatcherObject의 VerifyAccess() 메소드가 “InvalidOperationException”을 던지도록 되어 있다. (WPF는 쓰레드의 잘못된 접근을 막기 위해 VerifyAccess 메소드를 자주 호출한다.)
n 아래 코드를 보자. (Invalid Operation Exception이 발생한다.)
public partial class MainWindow : Window {
public MainWindow()
{
InitializeComponent();
Thread thread = new Thread(Update);
thread.Start();
}
public MainWindow()
{
InitializeComponent();
Thread thread = new Thread(Update);
thread.Start();
}
private void Update()
{
Thread.Sleep(TimeSpan.FromSeconds(5));
txtName.Text = "홍길동";
}
}
{
Thread.Sleep(TimeSpan.FromSeconds(5));
txtName.Text = "홍길동";
}
}
n 위 코드를 Dispatcher를 사용하면 아래와 같이 재작성 할 수 있다.
(Dispatcher의 BeginInvoke를 사용하면 해당 메소드의 실행이 마무리 될 때까지 블록킹 된다.)
private void Update()
{
// Simulate some work taking place Thread.Sleep(TimeSpan.FromSeconds(5));
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
txtName.Text = "홍길동";
}
);
{
// Simulate some work taking place Thread.Sleep(TimeSpan.FromSeconds(5));
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
(ThreadStart)delegate()
{
txtName.Text = "홍길동";
}
);
//this.Dispatcher.BeginInvoke(new Action(() =>
// {
// TxtName.Text = "Hello Geeks !";
// }));
}
}
Dispatcher.BeginInvoke() 메서드는 두 개의 파라미터가 있는데 첫 번째는 작업의 우선 순위를 나타내고 두 번째는 매개변수는 실행할 코드가 있는 메소드를 가리키는 대리자(Delegate) 이다.
댓글 없음:
댓글 쓰기