2018년 1월 29일 월요일

[WPF교육] WPF 멀티쓰레드 프로그래밍 & Dispatcher를 이용한 프로그래밍


1.5 WPF 멀티쓰레드 프로그래밍
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();
         }
        private void Update()
        {

            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 = 
"홍길동";
                        }
            );
//this.Dispatcher.BeginInvoke(new Action(() => 
        //    { 
        //          TxtName.Text = "Hello Geeks !"; 
        //    }));
            
}

Dispatcher.BeginInvoke() 메서드는 두 개의 파라미터가 있는데 첫 번째는 작업의 우선 순위를 나타내고 두 번째는 매개변수는 실행할 코드가 있는 메소드를 가리키는 대리자(Delegate) 이다.

댓글 없음:

댓글 쓰기