Microsoft MVP성태의 닷넷 이야기
WPF Multi Dispatcher 사용 시 hang 발생 [링크 복사], [링크+제목 복사],
조회: 19872
글쓴 사람
이성환 (vactorman at naver.com)
홈페이지
첨부 파일

안녕하세요
막히는 것이 있어 이렇게 오랜만에 질문 올립니다.


wpf 로 multi dispatcher 를 이용한 UI 처리 작업을 진행 중에 있는데요.

첨부한 예제의 UIEntrance 클래스를 이용해 스레드를 분리하여 처리하는 방식으로 작성을 하였습니다.

그런데 간헐적으로 Post 한 메시지들을 반응이 없는 경우가 발생합니다.


크게 두 가지 경우가 발생하는데요.

첫번째는 Post 시 넘긴 SendOrPostCallback 이 무시되는 것 처럼 보이는 경우입니다.

일단 스택을 따라가 보면     WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage

여기서 멈춰 있는데요. 이거는 그냥 메시지 펌핑을 하는 스레드를 표시하는 거라 의미있는 정보가 아닌 것 같습니다.

디버그 트레이스를 찍어서 표시를 해봐도 정상적으로 Post까지 잘 넘어 갔는데

간헐적으로 딱 저 상태에서 반응이 없는 상황이 발생합니다.
(Go 버튼을 제대로 눌렀는데 새 창이 안 뜨는 경우 확인하면 새로운 스레드가 생성되었지만 스택이 저 위치에서 멈춰 있습니다.)

그러다가 다시 Go 버튼을 누르면 새로운 스레드로 새 창이 열립니다.

창이 열리지 않고 스레드만 생성된 경우 살아있는 스레드 때문에 정상 종료가 안 되는 문제도 있습니다.

스레드 죽이는 거야 어떻게든 하면 되겠지만

GetMessage 에서 멈춰서 작업을 밀어 넣어도 반응이 없는 경우는 어떻게 해야할지 난감합니다.



또 다른 경우는

실제 업무에서 사용하는 부분에서 발생하는 문제인데

첫 번째와 같은 방식이지만 Window 를 show 하기 전까지 새로 생성된 dispatcher로 좀 더 많은 작업을 하는 것 정도의 차이입니다.

증상은 위와 같이 반응이 없는 것은 같으나 좀 더 진행된 상태의 스택이 표시되는 경우 입니다.
(이 부분은 첨부한 예제로 재현이 안 되네요...)

현상은 동일한데 아래와 같은 스택이 표시됩니다.

WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait
WindowsBase.dll!System.Windows.Threading.DispatcherSynchronizationContext.Wait(System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)    
mscorlib.dll!System.Threading.SynchronizationContext.InvokeWaitMethodHelper(System.Threading.SynchronizationContext syncContext, System.IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout)    
[Native to Managed Transition]    
mscorlib.dll!System.Threading.WaitHandle.InternalWaitOne(System.Runtime.InteropServices.SafeHandle waitableSafeHandle, long millisecondsTimeout, bool hasThreadAffinity, bool exitContext)    
mscorlib.dll!System.Threading.WaitHandle.WaitOne(int millisecondsTimeout, bool exitContext)    
mscorlib.dll!System.Threading.WaitHandle.WaitOne()    
PresentationCore.dll!System.Windows.Input.PenThreadWorker.WorkerGetTabletsInfo()    
PresentationCore.dll!System.Windows.Input.StylusLogic.GetDeviceCount()    
PresentationCore.dll!System.Windows.Input.StylusLogic.TabletDevices.get()    
PresentationCore.dll!System.Windows.Input.StylusLogic.RegisterHwndForInput(System.Windows.Input.InputManager inputManager, System.Windows.PresentationSource inputSource)    
PresentationCore.dll!System.Windows.Interop.HwndStylusInputProvider.HwndStylusInputProvider(System.Windows.Interop.HwndSource source)    
PresentationCore.dll!System.Windows.Interop.HwndSource.Initialize(System.Windows.Interop.HwndSourceParameters parameters)    
PresentationCore.dll!System.Windows.Interop.HwndSource.HwndSource(System.Windows.Interop.HwndSourceParameters parameters)    
PresentationFramework.dll!System.Windows.Window.CreateSourceWindow(bool duringShow)    
PresentationFramework.dll!System.Windows.Window.CreateSourceWindowDuringShow()    
PresentationFramework.dll!System.Windows.Window.SafeCreateWindowDuringShow()    
PresentationFramework.dll!System.Windows.Window.ShowHelper(object booleanBox)    
PresentationFramework.dll!System.Windows.Window.Show()    
Mango.Service.dll!Mango.Service.Services.TableManagerService.CreateTable<Mango.Table.Holdem.Views.HoldemTableWindow,Mango.Table.Holdem.ViewModels.HoldemTableContainerViewModel,Mango.Table.Holdem.Interfaces.IHoldemCallbackViewModel>.AnonymousMethod__1(System.Guid value) Line 53    
mscorlib.dll!System.Collections.Concurrent.ConcurrentDictionary<System.Guid,Mango.Define.Models.TableManagerInfoModel<System.Windows.Window>>.GetOrAdd(System.Guid key, System.Func<System.Guid,Mango.Define.Models.TableManagerInfoModel<System.Windows.Window>> valueFactory)    
Mango.Service.dll!Mango.Service.Services.TableManagerService.CreateTable<Mango.Table.Holdem.Views.HoldemTableWindow,Mango.Table.Holdem.ViewModels.HoldemTableContainerViewModel,Mango.Table.Holdem.Interfaces.IHoldemCallbackViewModel>(Mango.Define.Models.TableCreationModelBase creationModel) Line 25    
Mango.Table.Holdem.dll!Mango.Table.Holdem.Utilities.HoldemTableCreator.CreateTable(Mango.Table.Holdem.Models.HoldemTableCreationModel creationModel) Line 24    
!Mango.ViewModels.MonitoringLobbyWindowViewModel.ConnectAndCreateTable(Mango.Table.Holdem.Models.HoldemTableCreationModel creationModel) Line 361    
!Mango.ViewModels.MonitoringLobbyWindowViewModel.TableOpen(object da) Line 329    
[Resuming Async Method]    
System.Threading.Tasks.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start<Mango.ViewModels.MonitoringLobbyWindowViewModel..<TableOpen>b__d>(ref Mango.ViewModels.MonitoringLobbyWindowViewModel..<TableOpen>b__d stateMachine)    
System.Threading.Tasks.dll!System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start<Mango.ViewModels.MonitoringLobbyWindowViewModel..<TableOpen>b__d>(ref Mango.ViewModels.MonitoringLobbyWindowViewModel..<TableOpen>b__d stateMachine)    
!Mango.ViewModels.MonitoringLobbyWindowViewModel.TableOpen.AnonymousMethod__d(object da)    
Mango.Presentation.dll!Mango.Presentation.Entrance.UISynchronizationEntrance.Post.AnonymousMethod__2() Line 189    
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)    
WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler)    
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()    
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state)    
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)    
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)    
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)    
WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()    
WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()    
WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)    
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)    
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o)    
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)    
WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler)    
WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)    
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)    
[Native to Managed Transition]    
[Managed to Native Transition]    
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)    
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame)    
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run()    
Mango.Presentation.dll!Mango.Presentation.Entrance.UISynchronizationEntrance..ctor.AnonymousMethod__0() Line 95    
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state)    
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)    
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx)    
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)    
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart()    



중간 즈음에

Mango.Service.dll!Mango.Service.Services.TableManagerService.CreateTable<Mango.Table.Holdem.Views.HoldemTableWindow,Mango.Table.Holdem.ViewModels.HoldemTableContainerViewModel,Mango.Table.Holdem.Interfaces.IHoldemCallbackViewModel>.AnonymousMethod__1(System.Guid value) Line 53    

요부분에서 새 Window 를 Show() 하는데 StylusLogic 을 찾더니 Waiting에 빠져버립니다.

개발 환경은 Windows7에 stylus 장치 없이 순수한 Window 환경이고 .net 4.0 을 사용하고 있는데요.


유사한 증상이 있는 사람이 있긴 하더군요.

https://social.msdn.microsoft.com/Forums/vstudio/en-US/71543c67-bc3b-4df8-acb8-324a1b32e37b/random-hangup-when-showing-a-dialog-on-a-touchscreen-on-win8?forum=wpf

Window 를 Show 한 이후 스택이 유사해서 댓글에 달린 핫픽스를 찾아 설치해 보려 했는데 설치가 안 되더군요.
(설치한다고 증상이 없어진다는 보장도 없긴 하지만...)

근데 저는 터치 스크린 따위 없고 Win7 이라 환경이 유사하지는 않는데

Multi Dispatcher 를 이용해 여러 창을 띄워 제어하겠다는 의도로 만든 것이 이런 문제를 발생하는 건지... 참 난감합니다.

사실 이 증상 때문에 테스트 예제를 만든 건데 막상 위 두 번째 문제는 테스트 예제로 재현이 안 되네요. =ㅅ=;;;


아마도 두 번째 문제 부분은 정보가 부족해서 분석이 불가능하시리라 생각하지만

혹시 알고 계신 증상이라거나 아니면 이 정도로도 해결 방법을 알고 계실까 해서 올렸습니다.


제 생각에는 첫 번째와 두 번째가 완전히 다른 원인 같지는 않은 것 같은데

어찌됐든 한 가지라도 해결할 수 있었으면 좋겠다고 생각하고 있습니다.


염치 불구하고 혹시 한 번 봐 주실 수 있을 지 부탁드립니다.

바쁘실 텐데 죄송합니다.








[최초 등록일: ]
[최종 수정일: 7/9/2015]


비밀번호

댓글 작성자
 



2015-07-13 01시02분
2번 문제는 일단 차치하고, 1번 문제부터 봤을 때, 설명을 좀 더 자세하게 해줄 수 있을까요? 저는 지금 이성환 님의 작업을 한 번도 본 적이 없고 어떤 의도로 만들었는지도 모릅니다. 따라서, 상대방이 알고 있다는 가정아래 글을 쓰시면 안 됩니다.

먼저 문제 재현 방법부터 알아야 할 것 같은데요. 보내주신 소스 코드를 실행해서 "Go" 버튼을 누르면 실행이 잘 되는데... 어떤 문제가 있다는 것인지부터 설명이 필요할 것 같습니다. "Go" 버튼을 계속 누르다보면 응용 프로그램이 blocking이 되는 현상이 나온다는 것인가요? (참고로, _createdNum으로 작업 순서를 매긴 것은 잘못되었습니다. static 변수이기 때문에 스레드 간에 어떻게 변화할 지는 알 수 없습니다.)
정성태
2015-07-13 01시25분
[이성환] 제가 설명을 제대로 못 했나 봅니다. 죄송합니다.

1번 문제는 위에서 표현한 그대로 입니다. 매 Window 를 생성할 때마다 새로운 스레드로 Dispatcher를 만들어 수행하려고 하는 의도입니다.

그래서 Go 버튼을 누르면 새 스레드로 Dispatcher 를 생성해 Window를 띄우고 있는데
(중간에 Task.Run() 으로 하는 작업은 그냥 await 로 부하는 주기 위한 행동입니다. 아무 의미 없습니다.)

Go 버튼을 눌렀을 때 간헐적으로 아무 반응이 없는 경우가 생긴다는 얘기입니다.
(프로그램이 블럭되지 않는 않습니다. 단지 Go 버튼을 눌렀는데 아무 반응이 없다는 얘기입니다.)

이 때 아무 반응이 없을 때 브레이크를 걸고 스텍을 보면 분명 새 스레드가 생성된 후 WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage 까지 진입했지만

Post 했던 메시지들이 동작하지 않아 Window 가 뜨지 않은 상태로 남아 있습니다.

프로그램은 블럭되지 않고 다시 Go 버튼을 누를 수 있으며, Go 버튼을 누르면 여전히 새로운 스레드로 Dispatcher 를 생성해 잘 동작합니다.

문제는 앞서 얘기한 것처럼 간헐적으로 스레드가 정상적으로 생성되고 Post 로 메시지를 넘겼지만 아무 반응이 없는 경우입니다.

어떨 때는 한 두번 Go 버튼을 눌렀을 때 발생하기도 하고 또 어떨 때는 창을 50개 정도 띄운 후에 발생하기도 합니다.(말 그대로 간헐적입니다.)

테스트 프로그램에서는 그 빈도가 높지 않은데 실제 업무에선느 제법 높은 빈도로 나타나서 고민입니다.

아.. _createdNum 은 그냥 현재까지 만들어진 스레드 개수를 확인하는 용도 정도로 만들어 둔 것입니다. 순서나 작업에 대한 정보는 아니구요.

설명이 많이 부족한데 뭐라고 더 설명드릴 방법이....;ㅅ;

도움 부탁드립니다.
[guest]
2015-07-13 04시22분
이성환 님, 저도 현상이 재현되었습니다. 일단, 나름대로 여러 가지 테스트를 해본 후에 제가 내린 결론은, Dispatcher가 초기화 되기까지 제법 시간이 걸리는 듯합니다. Dispatcher.FromThread로 가져오긴 했지만, 그 시점이 DispatcherSynchronizationContext.Post하기에 안전한 시기는 아니라는 것입니다.

테스트를 위해, UIEntrance 객체를 미리 100개를 생성하고 Thread.Sleep을 1초 정도 준 후, 해당 UIEntrnace에 연이어 100개의 Post를 하면 정상적으로 메시지 처리가 되는 것을 볼 수 있습니다.

일단, 제가 해줄 수 있는 권고안은 이렇습니다. Dispatcher가 어차피 UI 요소를 호스팅하기 위한 것이므로 UIEntrance 생성자 내부에서 준비하는 Thread 내부에서 Dispatcher.Run을 하기 전 관련된 XAML Window도 함께 생성해서 호스팅 준비를 합니다. 그리고 해당 XAML Window의 Loaded 이벤트에서 DispatcherSynchronizationContext.Post를 호출하기에 안전하다는 flag를 두어(또는 EventWaitHandle.Set 객체로 신호를 주어) 이후의 작업을 진행하는 방식으로 변경하는 것입니다.

2번 문제는, ... 그 정도의 단서만으로는 저도 어쩔 수가 없군요. ^^
정성태
2015-07-14 01시44분
[이성환] 답변 감사드립니다.

저도 비슷하세 추측은 했지만 Dispatcher.FromThread 를 이용하는 거라 설마... 하고 있었습니다.(확인할 길이 없기도 했구요..;;;)
그래서 여러가지 시도를 하는 중이긴 한데.... 프로그램 진입점 에서 UIEntrancePool을 만들어서 사용하는 방식도 해보고 있고 답변 주신 플래그를 이용하는 방식으로도 해보고 있는데...
Pool 은 결국 cpu 낭비가 심하고 얼마나 생성될 지 예측을 하기 어려워서 포기했고 플래그 방식은 Window 뿐만 아니라 그 앞단에서 해줘야할 작업 같은 것들이 있어서
생각보다 잘 안 되네요...;;;

게다가 처음 의도는 개별 Window 대해 다른 작업의 영향을 받지 않게 Dispatcher를 분리해 수행하자 였는데
dispatcher 가 늘어날 수록 오히려 성능이 더 저하되는 현상이 발생하더군요. (컨텍스트 스위칭 때문에 그런 거 같다는 느낌이 들었습니다.)
이래 저래 머리 싸매다가 결국 다시 MainThread의 Dispatcher 로 모든 Window를 처리하는 방식으로 돌아가는 중입니다...;ㅅ;
(근데 Main dispatcher 로도 여러 Window 를 띄워서 복잡한 UI 행동을 하면 느리지는 건 마찬가지 입니다.)

wpf의 성능을 어떻게든 좀 향상 시켜보려고 발버둥 쳤는데 결국이 한계에 부딪힌 느낌입니다.

여튼 답변 감사드립니다. (__)
[guest]

... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...
NoWriterDateCnt.TitleFile(s)
353정성태4/20/200617909    답변글 [답변]: 이번겨울방학때세도나를 여행하려구하는데요,,,
351오기4/18/200610711aspx [2]
350유지연4/17/200611461smartclient DB connection관련 질문 입니다. [2]파일 다운로드1
349선재빠4/17/200611464object tag를 2개 넣으면 IE가 죽어버리네요. [2]
347박찬용4/13/200611485COM+에 관한 질문입니다. [2]
345선재빠4/11/200612298Winform을 띄울 수 있는 방법이 있나요? [3]
344선재빠4/10/200613551ASP.NET 2.0에서는 VIEWASTEXT 이란 태그옵션을 지원하지 않는군요 [3]
343선재빠4/10/200612661아래와 같은 에러가 나네요.
341선재빠4/8/200612357테스트 페이지를 보면 프레임만 나오고 안에 내용이 나오지 않는군요 [1]
340김형태4/4/200611521파일서버 관련 질문드립니다.. (데브피아 답변에 이어) [2]
339조남정4/4/200611687post 주소 읽어오기 [1]
338장두헌4/4/200613290SDK PlatForm 다시 질문 - 데브피아의 내용 [1]
337강완모4/3/200613573Cab파일이 아닌 설치용 파일로 OCX들 설치하기... [1]
335비니3/23/200614513밑에 Smart Client에 대해서 질문했던 초보입니다.. TreeView컨트롤은 보이는데 안에 내용이 안보여요. [1]
336비니3/24/200612431    답변글 [재질문]:자꾸 죄송합니다...링크페이지로 이동을 안해요.... [1]
334비니3/23/200612130Smart Client강의 따라해보다가 xml에서 활성스키마는 <Tree>,<L> 요소는 지원하지 않는다고 나와요.ㅠㅠ [1]
332정준명3/21/200612180[자문자답] VS.NET에서 COM을 참조했는데, 강력한 서명이 없다고 사용불가네요.
333정성태3/22/200610706    답변글 [답변]: [자문자답] VS.NET에서 COM을 참조했는데, 강력한 서명이 없다고 사용불가네요. [1]
330심현철3/3/200613831[질문] VS2005에서 C#으로 개발한 DLL을 Javascript에서 접근할 수 없습니다. (ActiveX with C#) [2]
329권인성2/27/200612016스마트클라이언트(clickonce) 버전관리 [1]
327권인성2/24/200611635clickonce를 통해 설치 및 업데이트하려고 하는데요...보안때문에요..
331정성태3/12/200612709    답변글 [답변]: clickonce를 통해 설치 및 업데이트하려고 하는데요...보안때문에요..
325권인성2/23/200611219SETUP 및 배포관련 [1]
326권인성2/23/200610702    답변글 [답변]: 한가지만 더 질문할게요.... [1]
323정보문2/21/200611037메일 내용에서 한글이 깨집니다. [1]파일 다운로드1
321셈토2/18/200610855SPOOL --> EMF --> TEXTOUT [1]
... 76  77  78  79  80  81  82  83  84  [85]  86  87  88  89  90  ...