2014-02-12 4 views
0

영어가 잘 못되어서 제 질문에 대해 간단하게 설명하고 문제를 설명하기 위해 여기에 코드 스 니펫을 붙여 넣으십시오.C# Application.DoEvents는 내 응용 프로그램에서 작동하지 않습니다.

문제는 우리의 winForm 응용 프로그램에서 여러 스레드 문제입니다. 나는 다음의 코드 샘플처럼 간단한 논리를 사용한다.

테스트 코드에는 mainForm에 1 개의 mainForm Form1과 "Start"라는 버튼이 있습니다. 사용자가 버튼을 클릭하면 2 개의 폼 form2와 form3이 2 개의 백그라운드 스레드에서 표시됩니다. form2가 닫히면 Form1이 닫히도록 트리거됩니다. 그러나 form3이 여기에 표시되므로 form3을 혼자서 닫아야합니다. 그래서 form.Closing 이벤트를 처리하고 Application.DoEvents()를 사용하여 사용자가 form3을 닫도록합니다. 그것은 마음에 작동 보인다. 그러나 실제로 form3은 사용자의 작업을 수락 할 수 있지만 form3은 예상대로 닫히지 않습니다.

여기서 form3을 닫을 수없는 이유와 사용자의 닫기 작업을 위해 코드를 수정하는 방법을 설명하십시오.

using System; 
using System.ComponentModel; 
using System.Threading; 
using System.Windows.Forms; 

namespace CloseFormFromMainThread 
{ 
public partial class Form1 : Form 
{ 
    private Form2 _form2; 
    private Form2 _form3; 
    private SynchronizationContext _synchronizationContext; 

    public Form1() 
    { 
     InitializeComponent(); 
     Closing += Form1Closing; 
    } 

    void Form1Closing(object sender, CancelEventArgs e) 
    { 
     while (_form3 != null) 
     { 
      Application.DoEvents(); 
      Thread.Sleep(100); 
     } 
    } 

    private void ButtonStartClick(object sender, EventArgs e) 
    { 
     var thread = new Thread(StartForm3); 
     thread.Start(); 

     var thread2 = new Thread(StartForm2); 
     thread2.Start(); 
    } 

    private void StartForm3() 
    { 
     Thread.Sleep(200); 
     var action = new Action(() => 
            { 
             _form3 = new Form2(); 
             _form3.Text = "form 3"; 
             _form3.ShowDialog(); 
             _form3 = null; 
            }); 
     ExecuteActionInUiThread(action); 
    } 

    private void Form1Load(object sender, EventArgs e) 
    { 
     _synchronizationContext = SynchronizationContext.Current; 
    } 

    private void StartForm2() 
    { 
     Thread.Sleep(500); 
     var action = new Action(() => 
     { 
      _form2 = new Form2(); 
      _form2.Text = "form 2"; 
      _form2.ShowDialog(); 

      Close(); 
     }); 
     ExecuteActionInUiThread(action); 
    } 

    private void ExecuteActionInUiThread(Action action) 
    { 
     var sendOrPostCallback = new SendOrPostCallback(o => action()); 
     _synchronizationContext.Send(sendOrPostCallback, null); 
    } 
} 
} 
+2

왜 스레드를 사용하고 있습니까? 모든 것이 UI 스레드에서 실행되는 것 같습니다. 1 단계는 모든 불필요한 복잡성을 없애고 스레드를 제거하는 것입니다. 그렇다면 프로그램이 실제로하는 일을 볼 기회가 있습니다. –

+2

실제 코드에는 Thread.Sleep()이 필요하지 않습니다. 그리고 GUI는 항상 1 스레드이며, 그렇지 않으면 시도하지 마십시오. –

+0

@HenkHolterman : 작동하지 않습니다. WinForms는 다중 스레드를 지원하지 않습니다. 그냥 추락 할거야. – PMF

답변

2

첫 번째 제안 : Application.DoEvents()를 사용하지 마십시오. 이제까지. 필요하다고 생각할 때마다 먼저 코드 플로우에 개념적으로 문제를 해결해야합니다. 나는 당신의 코드가 다른 이벤트를 처리하기 전에 (OnClosing 콜백이 반환되기를 기다리는 (다른 폼이 닫히기를 기다리기 때문에) 데드락을 생성하는 것 같다.

+0

신청서를 삭제하면.DoEvents(), 대화 상자에서 사용자의 클릭 조작을 허용하고 닫기 조작을 처리하는 방법은 무엇입니까? – user3256047

+1

(ShowDialog()를 사용하여) 모달 모드에서 대화 상자를 여는 중입니다. 그러면 자동으로 메시지 루프가 실행되고 이벤트가 처리됩니다. – PMF

+0

form3.Close() 메서드가 실행되었지만 form3.ShowDialog()가 반환되지 않았습니다. 왜? – user3256047

0

모달 대화 상자에서 Form.Close()의 소스 코드를 확인하면 FormClosing 이벤트 만 발생하고 Closed 이벤트는 발생하지 않습니다. form3.ShowDialog가 진행되도록 신호가 보내지지 않았습니다. 그래서 나는 오직 메인 쓰레드만을 사용하고 Application.DoEvents는 코드를 진행시킬 수 없다고 생각한다. 그러면 주 스레드에서 부드러운 교착 상태가됩니다.

현재 다른 스레드를 사용하여 _form3! = null을 확인하고 주 스레드가 _form3.ShowDialog() 논리를 실행하도록합니다. 다음은 Form1Closing에 대한 내 코드입니다.

private bool _isFormClosing; 
    void Form1Closing(object sender, CancelEventArgs e) 
    { 
     if (_form3 == null) return; 

     e.Cancel = true; 
     if (!_isFormClosing) 
     { 
      _isFormClosing = true; 
      Task.Factory.StartNew((() => 
      { 
       while (_form3 != null) 
       { 
        Thread.Sleep(50); 
       } 

       ExecuteActionInUiThread(Close); 
      })); 
     } 
    } 
+0

이 작업을 수행하지 마십시오. 배경 스레드에서 양식을 여는 것은 매우 나쁜 생각입니다. Windows 탐색기와 같은 다중 스레드 UI 응용 프로그램을 만들지 않는 한 각 UI 스레드는 서로 독립적입니다. – Noseratio

+0

내 코드에서는 모든 UI가 Ui 스레드로 작성/닫힙니다. – user3256047

관련 문제