Microsoft MVP성태의 닷넷 이야기
WPF Multi Dispatcher 사용 시 hang 발생 [링크 복사], [링크+제목 복사]
조회: 6038
글쓴 사람
이성환 (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 은 그냥 현재까지 만들어진 스레드 개수를 확인하는 용도 정도로 만들어 둔 것입니다. 순서나 작업에 대한 정보는 아니구요.

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

도움 부탁드립니다.
[손님]
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의 성능을 어떻게든 좀 향상 시켜보려고 발버둥 쳤는데 결국이 한계에 부딪힌 느낌입니다.

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

... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...
NoWriterDateCnt.TitleFile(s)
3604popo9/11/20154334[C#] Singleton 을 사용하면서 궁금한점이 있어 질문 남깁니다. [2]
3603강준9/11/20153648xamarin 을 시작하려고 하는데 혹시 도움이 될만한 사이트나 정보 있으면 공유해 주세요~~ [1]
3602Minky Ko9/9/20154644aspx > 다른 확장자로 변경 후 디버깅 때문에 질문 드려 봅니다. [10]
3601edain9/8/20154919캐스팅...에 관해 궁금합니다. [5]
3600손님9/7/20157184C# 버전의 정의는 어떻게 구분하나요? [2]
3599김태훈9/4/20154603안녕하세요. C언어를 배우려고 하는 문과생입니다. [1]
3598미니8/29/201542606.0은 전체소스? [1]
3597손성수8/28/20153991시작하세요 ! c# 프로그래밍 이책으로 배우고 있는데요 [2]
3596지나가는 손님8/26/20153867크로스플랫폼 [1]
3594손님8/25/20154112요즘 Windonws Form 기반 N Tier 어떻게 구성하나요?? [1]
3593개발자8/18/20155552현 시점에서 VS 버전은 뭐가 좋을까요? [8]
3592나그네8/15/20154605안녕하세요 질문이 있어서... [4]
3589spowner8/11/20155694Nancy + Razor 사용시 cshtml의 IntelliSense 및 참조 오류 [3]
3590spowner8/12/20155143    답변글 [질문]: (첨부 추가) [1]파일 다운로드1
3588재현8/8/20157798안녕하세요. C# 6.0 책을 보는 중에 의문이 생겨 질문드립니다. [4]
3587주문중8/4/20157373신간 출간 [7]
3586꿈꾸는개발자7/29/20154757자바스크립트 checkbox 관련하여 ie10에서 호완이 안되는 부분이 있어 질문드립니다 [1]
3585유동근7/21/20153877TTS오류 [1]파일 다운로드1
3583노영우7/20/20154974iisnode 를 클래식 파이프라인 모드에서 호스팅하기 [2]
3582황희성7/17/20157333동영상 캡쳐에 관련해서 궁금하것이 있습니다. [6]파일 다운로드1
3581popo7/15/20154686[WPF] DependencyProperty에서 callback 처리시 문의 입니다. [1]
3580국왕님7/14/20154669어셈블리 서명 pfx 파일의 가져오기시 암호를 확인할 수 있을까요? [1]
3579고훈용7/13/20154953스마트클라이언트 윈도우8.1 실행오류 해결 방법 문의 [1]파일 다운로드1
3578이성환7/9/20156038WPF Multi Dispatcher 사용 시 hang 발생 [4]파일 다운로드1
3577초보개발자7/9/20156072C# SHDocVw.InternetExplorer 관련 도움좀 부탁드리겠습니다. [1]
3576솔솔7/6/20154780zip압축시! [2]
... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...