Microsoft MVP성태의 닷넷 이야기
WPF Multi Dispatcher 사용 시 hang 발생 [링크 복사], [링크+제목 복사]
조회: 13045
글쓴 사람
이성환 (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]

... 31  [32]  33  34  35  36  37  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
5068누오10/10/20187653ASP Core 2.0 에서 dll안에 있는 뷰 읽어들이는 방법? [1]
5067김정민10/5/20187634다른 윈도우가 깨지는 현상을 막을 수 있을까요 [3]
5066로니브10/4/20188427ASP.NET MVC에서 View 파일 숨기는법? 보안처리 하는법? 관련 질문.. [1]
5065키모10/1/20189672문자 질문입니다. [3]
5064로니브10/1/20188947클래스 라이브러리에서 .cshtml파일을 추가하는 방법은 없나요? [3]
5063진우9/28/20187968ADO.net 과 Entity Framework 차이 문의 [2]
5062테스트9/27/20188572C# import file 의 구조체 배열 선언 및 호출에 대해 문의. [3]
5061안녕하세요9/13/20188491c# 프로그래밍 관련 문의 [1]
5060임민재9/8/20187984c# install 파일 생성 시 문제가 발생하였습니다 [1]파일 다운로드1
50599/7/20187670Winform TextBox 포커스 유지하는 방법 질문 [파일첨부] [1]파일 다운로드1
50589/5/201811066Winform TextBox 포커스 유지하는 방법 질문 [3]
5056박종윤8/30/201810493c# dll을 C++에서 사용 시 event 호출 [4]파일 다운로드1
5055초보자8/29/20189018asp.net 에서 다른 서버의 iis를 stop하는 batch file을 실행시키는데 동작하지 않습니다. [5]
5054사도신8/29/20187736[wpf] textbox insert overite 모드시에 [4]파일 다운로드1
5053엿장수8/26/20187108directshow filter 에서의 IMediaSample 의 시간에대한질문입니다 [1]
5052오명현8/26/20187995Tcp소켓 실습 Exeption 도와주세요! [4]파일 다운로드1
5049오명현8/23/20187359책 477페이지 내용 중 이해가 안가는 부분이 있어 질문드립니다. [1]
5048오명현8/23/20187062포트 관련 질문 하나더 있습니다. [1]
5047오명현8/22/20187756포트가 없을 경우를 가정한 내용에 대해 질문이 있습니다. 책468p. [1]
5046엿장수8/22/20187673다이렉트쇼 필터 추가하는데 [2]
5045임도진8/22/20188389c# opencv dll파일 로드 질문 [3]파일 다운로드1
5044엿장수8/20/20188090graphedit 에 등록되어있는 필터를 가져와서 사용하는방법을 알고싶습니다 [2]
5043hori...8/20/20187722Expression에 대한 책의 예제 관련하여 질문드립니다. [1]
5042황윤하8/20/20189436모드버스 TCP 관련 질문 [3]
5041david8/18/20189114.NET WebAPI 에서 Response 지연되는 이유 [3]
5040농상8/14/20188545정규표현식에 대해서 [3]
... 31  [32]  33  34  35  36  37  38  39  40  41  42  43  44  45  ...