2017-02-11 12 views
4

우리는 C#으로 멀티 스레드 게임 엔진을 개발 중입니다. 우리는 STAThread 속성이 필요하거나 수동으로 스레드를 STA에 설정해야 드래그 앤드 드롭 지원 (AllowDrop은 STA없이 설정할 수 없습니다). 그러나 STA를 사용하고 업데이트 방법이 draw 방법보다 오래 걸리는 경우 (아래에 설명) 창의 창이 제대로 작동하지 않습니다. 작업 표시 줄에서 클릭 할 때 예상대로 최소화 및 최대화되지 않습니다. 그것. 정확한 동작은 서로 다른 시스템에서 다르기 때문에 어떤 종류의 경쟁 조건이 여기에 작용할 것입니다.STA를 사용할 때 이상한 행동을하고 스레드가 너무 오래 걸리는 경우

나는이 문제를 인식
[STAThread] 
    public static void Main() 
    { 
     Form form = new Form(); 
     form.Show(); 

     Barrier barrier = new Barrier(2); 

     Thread updateThread = new Thread(() => { 
      while (true) 
      { 
       barrier.SignalAndWait(); 
       Thread.Sleep(30); //Update 
       barrier.SignalAndWait(); 
      } 
     }); 
     updateThread.Start(); 

     while (true) 
     { 
      barrier.SignalAndWait(); 
      Thread.Sleep(15); //Draw 
      barrier.SignalAndWait(); 
      Application.DoEvents(); 
     } 
    } 

답변

5

, 내가 관리되지 않는 응용 프로그램에 다시 비스타의 일에 매우 유사한 문제를 디버깅 :

는 여기에 우리의 테스트 코드입니다. 이 문제는 매우 모호하며 작업 표시 줄 단추를 클릭하여 생성하는 매우 까다로운 WM_ACTIVATEAPP 메시지와 관련이 있습니다. 특히 중첩 된 메시지 루프에 의해 디스패치되는 경우 해당 루프는 특정 "안전한"메시지 만 전달되도록 메시지 필터링을 수행합니다.

Barrier.SignalAndWait() 호출로 인해 발생합니다. 그 UI 스레드를 차단하고 STA 스레드가 불법입니다. CLR은 기본 동기화 개체가 신호를 보내지 않는 동안 자체 메시지 루프를 펌핑합니다. 인 경우 루프가 WM_ACTIVATEAPP를 전달하고 30 밀리 초 동안 차단하고 한 번만 펌프를 사용하기 때문에 확률이 매우 높습니다. 그러면 잘못되었습니다. 몇 가지 신비한 이유 때문에 후속 메시지가 발송되지 않습니다. 거의 확실하게 그 메시지 루프에 의해 수행 된 필터링에 의해 야기됩니다. 그렇지 않으면 볼 수 없기 때문에이 코드는 게시되지 않았고 디 컴파일이 불가능했습니다.

고칠 수는 있지만 쉽게는 아닙니다. 이 문제를 해결하려면 CLR을 이 아니고이 메시지 루프 펌프로 보내야합니다. 기다림이 짧아서 괜찮습니다. SynchronizationContext.Wait() 메서드를 재정 의하여 수행 할 수있는 작업입니다. 안타깝게도 그렇게하기가 어렵습니다. WindowsFormsSynchronizationContext 클래스는 봉인되어 있으므로 Wait() 메서드를 재정의 할 수 없습니다. Reference Source을 방문하여 클래스에 코드를 복사/붙여 넣기해야합니다. 다른 이름을 써. (즉, SetWaitNotificationRequired을 한마디로

System.ComponentModel.AsyncOperationManager.SynchronizationContext = 
     new MySynchronizationContext(); 

:

using System.Runtime.InteropServices; 

class MySynchronizationContext : SynchronizationContext { 
    public MySynchronizationContext() { 
     base.SetWaitNotificationRequired(); 
    } 
    public override int Wait(IntPtr[] waitHandles, bool waitAll, int millisecondsTimeout) { 
     return WaitForMultipleObjects(waitHandles.Length, waitHandles, false, millisecondsTimeout); 
    } 

    [DllImport("kernel32.dll")] 
    static extern int WaitForMultipleObjects(int nCount, IntPtr[] lpHandles, 
     bool bWaitAll, int dwMilliseconds); 
} 

과 함께 새로운 동기 프로 바이더를 설치

난 당신이 바꿀 필요가 무엇인지 보여, 그것의 짧은 버전주지) 호출은 자신의 Wait() 구현을 사용하는 대신 Wait() 재정의를 호출해야한다고 CLR에 알립니다. 그리고 Wait() 오버라이드는 펌핑하지 않고 OS의 블로킹 대기를 사용합니다. 내가 그것을 테스트하고 문제를 해결했을 때 잘 작동했다.

+0

흠, 먼저 우리는 Barrier.SignalAndWait에서 벗어나서 별도의 스레드를 사용하여 응용 프로그램을 실행하려고합니다. 이벤트 루프가 실행되는 동안 DirectX/OpenGL이 표시 될 수 있는지 여부를 확인해야합니다. 그렇지 않으면, 우리는 귀하의 접근 방식을 시도 할 것입니다. 나는 우리가 시험을 마치면 (즉 오늘 저녁에) 당신의 답을 받아 들일 것입니다. – georch

+0

이제 우리는 update, draw 및 DoEvents에 별도의 스레드를 사용합니다. 그게 더 쉬운 솔루션이었고, 또한 우리는 STAThread (Cursor.Hide와 Cursor.Show 같은 호출은 때때로 실행 중에 호출되어야 함)를 필요로하는 다른 호출을 만들 수 있습니다. 도움을 많이 주셔서 감사합니다! – georch