2011-11-25 2 views
4

내 응용 프로그램에서 숨겨진 dummyForm이라는 숨겨진 시작은 기본 UI 스레드를 추적하기 위해 작성된 것입니다. 따라서 새 양식을 만들려고하면 더미 양식에서 InvokeRequired을 사용하여 새 양식을 만들 때 기본 UI 스레드에 있는지 확인하십시오.WinForms를 사용한 스레딩?

frmStart 양식을 직접 인스턴스화 한 후에 나는 frmStart.InvokeRequired을 확인하고 false로 설정되므로 여기에서 호출 할 필요가 없습니다 (동일하게는 dummyForm.InvokeRequired).

frmMyDialog.InvokeRequired = false 
dummyForm.InvokeRequired = false 
frmStart.InvokeRequired = true 

:

using(Create frmMyDialog on main UI thread) 
{ 
    frmMyDialog.Show(frmStart); 
} 

이 크로스 스레드 예외 여기에 이상한 일을 던질 것입니다 :

그 다음이 같은 부모/소유자 무언가로 frmStart를 사용하는 frmMyDialog있어 그리고 이것은 이 거짓 일 때 frmStart을 만들 때 거짓인지 확인하는 중입니다.

frmMyDialog.InvokeRequired은 항상 dummyForm.InvokeRequired과 같아야합니다. 여기서 무슨 일이 일어나고있는거야?

첫 번째 인스턴스를 만든 후에 frmStartdummyForm이 전혀 다시 생성되지 않았 음을 확인했습니다.

EDIT1 :

이 응용 프로그램이 시작하는 방법입니다 MyClientMain 클래스의 생성자는 MainControl라는 정적 클래스에서 설치 프로그램을 실행합니다

public static void Main(string[] args) 
{ 
    _instance = new MyClientMain(parameters); 
    Application.Run(_instance); 
} 

. MainControler는 설정 방법이 같은 dummyform합니다 실체화 :

if (_dummyForm == null) 
    _dummyForm = new Form(); 

이 후에는 로그인 양식은 로그인을 처리 할 수행이 양식은 멀티 스레드입니다. 로그인이 끝나면 MainController가 다시 호출되어 frmStart가 들어있는 기본 MDI windo를 인스턴스화하고 엽니 다. 우리가 같은 스레드에 있는지 확인하려면 다음을 수행하십시오.

public static StartApplication() 
{ 
if (_dummyForm.InvokeRequired) 
        _dummyForm.Invoke(new MethodInvoker(delegate { OpenMainOrbitWindow(); })); 
    //Instanciate mainform and frmStart then open mainForm with frmStart as a MDI child 

} 

여기에 멀티 스레딩이 없습니다.

그런 다음 서비스가 오프라인 상태가되면 이벤트가 트리거되고 frmMyDialog를 팝업해야하지만 사용하면됩니다.ShowDialog를()는 대화 상자가 다음과 같이 부모/소유자를 찾을 수 최우선 형태의 뒤에 배치 및 설정됩니다 다음을 생성 시작 및 :

public static Form GetActiveForm() 
     { 
      Form activeForm = Form.ActiveForm; 

      if (activeForm != null) 
       return activeForm; 

      if (MainOrbitForm.TopMost) 
       return MainOrbitForm; 
      else 
      { 
       FormCollection openForms = Application.OpenForms; 
       for (int i = 0; i < openForms.Count && activeForm == null; ++i) 
       { 
        Form openForm = openForms[i]; 
        if (openForm.IsMdiContainer) 
         return openForm.ActiveMdiChild; 
       } 
      } 

      if (_patientForm != null) 
      { 
       if (_patientForm.TopMost) 
        return _patientForm; 
      } 

      return null; 
     } 

     public static string ShowOrbitDialogReName() 
     { 
      frmMyDialog myDialog; 
      Form testForm; 

      //Makes sures that the frmOrbitDialog is created with the same thread as the dummyForm 
      //InvokeRequired is used for this 
      using (myDialog = MainController.CreateForm<frmOrbitDialog>()) 
      { 
       //Settings... 
       testForm = GetActiveForm(); 
       myDialog.ShowDialog(GetActiveForm(testForm)); 


      } 
     } 

문제는

myDialog.InvokeRequired = false 
testForm.InvokeRequired = true; 
MainController.DummyForm.InvokeRequired = false; 

Edit2가 있다는 것입니다 dummyform : 우리가 mainform을 만들 성공 로그인 후

dummyForm.InvokeRequired = false 
Thread.CurrentThread.ManagedThreadId = 9 

_mainForm.InvokeRequired = false 
MainControl.DummyForm.InvokeRequired = false 
Thread.CurrentThread.ManagedThreadId = 9 

지금까지 모든 것이 잘 보입니다. 그런 다음 콜백 (WCF)를 수신하고 이벤트는 동일한 스레드에 frmMyDialog (호출이 dummyForm에 사용) 후 ShowDialog를 사용하는 생성 :이 CrossThreadException을 던졌습니다

frmMyCustomDialog.ShowDialog(_mainForm) 

를이는 모습입니다 이 시점에서 같이 :

_mainForm.InvokeRequired = true 
frmMyCustomDialog.InvokeRequired = false 
MainControl.DummyForm.InvokeRequired = false 
Thread.CurrentThread.ManagedThreadId = 12 

MainControl.DummyForm이 true가 아닌 이유는 무엇입니까? ManageThreadId는 9가 아니라 12입니까?

+0

더보기 * 예외 상황과 관련된 양식을 작성하는 방법에 대한 * 실제 * 코드를 표시하십시오. InvokeRequired는 주 UI 스레드가 아니라 컨트롤/폼이 작성된 스레드에 바인딩됩니다! – Jan

+0

Application.Run을 여러 번 호출합니까? 난 당신이 단지에 관계없이 상태의 호출을 강제하면 어떻게됩니까에서 :) – Jodrell

+0

@Jodrell 탄원 EDIT1 – Banshee

답변

2

System.Threading.SynchronizationContext.Current을 사용해야합니다. 이런 목적으로 직접 만들어졌습니다. 앱의 첫 번째 양식을 만든 후에는 어디서나 액세스 할 수 있습니다. 아래 예문에 의해 판단 해 보아도, 이것은 신청서를 작성하는 즉시 양식을 작성하기 때문에 문제가되지 않습니다. 어디 그런

public static void Main(string[] args) 
{ 
    _instance = new MyClientMain(parameters); 
    Application.Run(_instance); 
} 

, 당신은, 당신은 단순히 SynchronizationContext에보고 smartenough 것을,

System.Threading.SynchronizationContext.Current.Send() // To execute your code synchronously 
System.Threading.SynchronizationContext.Current.Post() // To execute your code synchronously 

마음을 사용하면 UI 스레드에서 이미 부르는, UI 스레드에서 실행되는 코드가 필요하고, 그러면 단순히 대리인을 직접 실행하게됩니다. 또한 SynchronizationContext를 처음 사용하기 전에 일부 WinForms 폼 또는 컨트롤을 만들어야한다는 것을 기억하십시오. 이렇게하면 컨텍스트가 적절한 구현으로 초기화됩니다.

기본값은 아무 것도하지 않습니다. 항상 코드를 동기화하여 실행합니다. WinForms 컨트롤 또는 WPF 컨트롤을 만들 때까지 현재 상태로 유지됩니다. Current에는 Winforms의 컨텍스트 또는 WPF 디스패처의 컨텍스트가 채워집니다.

+0

Thanks,하지만 System.Threading.SynchronizationContext.Current가 생성되고 양식을 연 후에도 null입니까? – Banshee

+0

흠, 분명히 SynchronizationContext는 스레드 당 싱글 톤이며 frmMyDialog가 표시 될 때 다른 스레드에있는 것처럼 보입니다. – Banshee

+0

양식을 표시해야합니다. –

0

예를 들어 멀티 스레딩에 대한 내용이 실제로 표시되지 않기 때문에 질문을 완전히 이해하지 못했습니다. 그러나 부모가 다른 스레드의 다른 양식 인 양식을 만들려면 다음 코드를 사용할 수 있습니다.

public void CreateShowDialogForm() 
{ 
    if (this.InvokeRequired) 
    { 
    this.Invoke(new Action(CreateShowDialogForm)); 
    } 
    else 
    { 
    Form frmMyDialog = new Form(); 
    frmMyDialog.Show(this); 
    } 
} 

private void Form4_Load(object sender, EventArgs e) 
{ 
    Task t = new Task(() => CreateShowDialogForm()); 
    t.Start(); 
    t.ContinueWith(task => true); 
} 
+0

옳지 않다는 것을 확신하지 못했습니다. Edit1을 살펴보십시오. – Banshee

1

이것은 내 머리 꼭대기에서 떨어져 있지만 블라디미르 페레 발로프 (Vladimir Perevalov)는 다른 토론에서 말했듯이 폼을 볼 수 있도록해야합니다.

frmDummy가 표시되지 않으면 창의 핸들이 생성되고 할당되지 않으므로 "InvokeRequired"에 항상 False로 응답합니다. 이것은 frmDummy를 통해 동기화하려는 모든 코드가 실제로 초기 UI 스레드로 전송되지는 않지만 항상 현재 스레드에서 실행된다는 것을 의미합니다. (방금 만든 컨트롤에 대한 자체 UI 스레드가됩니다).

중요한 점은 InvokeRequired는 상기 제어의 창 핸들이 다른 스레드가 소유하고 있는지 확인하려고 할 것입니다. 생성자와 아무 관련이 없습니다. 당신이 frmDummy를 표시하지 않으려면

, 당신은이 할당 핸들 것이 있는지 확인하기 위해, 당신이 그것을 인스턴스화 직후 CreateControl에 전화를 걸 수 있습니다.

관련 문제