2019년 6월 3일 월요일

C# BeginInvoke/Invoke 동작 원리

c#으로 개발한 UI 기반 프로젝트에서 BeginInvoke/Invoke 에 대해 알아보자.

간단한 동작원리이지만 개발자들이 의외로 잘 모르고 사용하는 경우가 많다.
먼저 아래 링크에 아주 잘 설명되어 있다.

https://www.codeproject.com/Articles/10311/What-s-up-with-BeginInvoke

위 링크에서 하는 설명의 결론을 말하자면 컨트롤에서 BeginInvoke/Invoke 를 호출하는 순간 UI 메시지큐에 사용자 정의 메시지가 push 되고 해당 메시지에 대해 실행되는 메시지 핸들러 함수가 등록되는 원리이다.

외부 쓰레드에서 UI 쓰레드에서 실행되는 delegate 를 등록하여 비동기 실행을 하도록 한다는 것이다.
아래 폼을 보자.




폼 위에 여러가지 UI 요소들이 있다. 여기에 UI 쓰레드가 몇 개가 돌까?
정답은 1개 이다. 특별히 쓰레드를 생성해서 폼을 생성하지않는한 UI 쓰레드는 언제나 한 개이다.
이 말은 win32 API 로 프로그램을 개발했다고 가정했을때 메시지 큐를 처리하는 메시지 루프가 한 개 돌고있다는 말이다.

BeginInvoke를 호출할때 굳이 btnOpen.BeginInvoke 니 textStatus.BeginInvoke 등 처럼 UI 컨트롤 변수를 지정해줄 필요가 없으며 의미도 없다는 말이된다. (WPF 의 Dispatcher 도 마찬가지이다)
그냥 this.BeginInvoke 혹은 BeginInvoke 로 호출하면된다.

Invoke 의 경우 쓰레드 동기화를 위해 이를 호출한 외부 쓰레드에서 delegate 타입의 이벤트 핸들러 함수가 끝날때까지  Sleep 상태로 기다렸다가 함수 실행이 끝나면 다음 코드가 진행되는 방식이다.

Invoke 함수의 경우 폼이 종료된 후 호출되면 UI  쓰레드가 종료된 이후이기 때문에 데드락에 빠지는 경우가 생긴다. 따라서 분명한 이유가 없으면 Invoke보다 BeginInvoke 사용을 권장한다.