2010-12-08 5 views
3

양식을 닫는 방법과 배경 작업자가 상호 작용하는 것으로 의심되는 일부 코드에서 이상한 버그를 관찰하고 있습니다. 여기 백그라운드 작업자를 사용할 때 양식 닫기를 올바르게 처리하는 방법은 무엇입니까?

는 잘못 잠재적 코드 :

var worker = new BackgroundWorker(); 
    worker.DoWork += (sender, args) => { 
     command(); 
    }; 
    worker.RunWorkerCompleted += (sender, args) => { 
     cleanup(); 
     if (args.Error != null) 
      MessageBox.Show("...", "...", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); 
    }; 
    worker.RunWorkerAsync(); 

이 코드는 버튼이 눌러지는 형태의 방법으로 실행된다. command()가 느리면 실행하는 데 몇 초 정도 걸릴 수 있습니다.

사용자가 위의 코드를 실행하는 버튼을 누르면 실행됩니다. 완료되기 전에 양식이 닫힙니다.

문제는 cleanup()을 으로 호출하면 가끔이 발생하여 ObjectDisposedException이 발생합니다. 나는 "때때로"라고 말합니다. 이것은 내 컴퓨터에서 결코 일어나지 않기 때문입니다. command()가 완료되기 전에 양식이 닫히면 RunWorkerCompleted에 등록 된 처리기가 실행되지 않습니다. 다른 컴퓨터에서 처리기는 한 번 백 번 호출됩니다. 동료의 컴퓨터에서 거의 항상 호출됩니다. 분명히 처리기의 실행 확률은 컴퓨터의 나이/느려짐에 따라 증가합니다.

첫 번째 질문 :

이것은 BakgroundWorker의 예상되는 동작입니까? 양식에 대해 아무 것도 모른다는 것을 기대하지는 않습니다. "근로자"와 "이"양식을 연결하는 것을 볼 수있는 것이 아무것도 없기 때문입니다.

두 번째 질문 :

문제를 해결하려면 어떻게해야합니까? 내가 고려하고

가능한 해결책 :

  1. 시험의 경우 정리()를 호출하기 전에 (this.IsDisposed!). 그렇게해도 충분합니까? 아니면 정리가 실행되는 동안 양식을 처리 할 수 ​​있습니까?
  2. try {} catch (ObjectDisposedException)에서 cleanup()에 대한 호출을 래핑하십시오. 나는 그런 종류의 접근법을 너무 좋아하지 않는다. 왜냐하면 나는 정리 (또는)에서 다른 관련이없는 버그로 인해 발생 된 예외를 잡을 수도 있고, 호출하는 메소드 중 하나를 예외로 잡을 수도 있기 때문이다.
  3. IsClosing 및 지연에 대한 처리기를 등록하거나 RunWorker Completed에 대한 처리기가 실행될 때까지 닫기를 취소하십시오.

관련이있을 수있는 추가 정보 : command()의 코드는 "this"의 GUI 개체에 대한 업데이트를 수행합니다. 이러한 업데이트는이 F 번호 함수 호출을 통해 수행됩니다 : 당신은 하나 개의 스레드 (작업자)에 의해 배치 된 컨트롤에 액세스하려고한다는 사실 이외의 BackgroundWorker에 대한 연결을하지 않아도 언급

/// Run a delegate on a ISynchronizeInvoke (typically a Windows.Form). 
let runOnInvoker (notification_receiver : ISynchronizeInvoke) excHandler (dlg : Delegate) args = 
    try 
     let args : System.Object[] = args |> Seq.cast |> Array.ofSeq 
     notification_receiver.Invoke (dlg, args) |> ignore 
    with 
     | :? System.InvalidOperationException as op -> 
      excHandler(op) 
+0

당신이 던진 GUI 업데이트 중 하나가 아니 었습니까? – CodesInChaos

+0

당신은 "양식에 대해 아무 것도 모른다는 것을 기대하지 않을 것입니다. 제가 볼 수있는 것이"this "와"this "를"worker "와 연결시키는 것을 볼 수 없기 때문입니다." 그러나'명령'과'정리'가 아마 형태의 방법인가? – Jordi

+0

CodeInChaos : command()에서 던져진 것이라면 args.Error의 예외와 함께 RunWorkerCompleted가 발생해야합니다. 그것은 여기서 일어나지 않습니다. – Joh

답변

4

예외 다른 스레드 (UI).

내가 사용하는 솔루션은 Form.FormClosed 이벤트에 이벤트 처리기를 연결하여 UI가 해체되었다는 것을 나타내는 플래그를 설정하는 것입니다. 그런 다음 RunWorkerCompleted 핸들을 사용하면 양식을 사용하여 작업을 시도하기 전에 UI가 해체되었는지 확인합니다.

양식을 명시 적으로 처리하지 않는 경우 IsDisposed을 확인하는 것보다이 방법이 더 효과적 일 수 있지만 정리 코드가 검사 된 직후에 양식이 닫히지 않거나 폐기되지 않도록 100 % 보장하지는 않습니다 그 깃발은 아직 거기 있다는 것을 발견했다. 이것은 자신이 언급 한 경쟁 조건입니다.

// set this to new object() in the constructor 
public object CloseMonitor { get; private set; } 

public bool HasBeenClosed { get; private set; } 

private void Form1_FormClosed(object sender, FormClosedEventArgs e) { 
    lock (this.CloseMonitor) { 
     this.HasBeenClosed = true; 
     // other code 
    } 
} 

을하고 작업자 :

이 경쟁 조건을 제거하려면 다음과 같이 예를 들어, 동기화해야합니다

worker.RunWorkerCompleted += (sender, args) => { 
    lock (form.CloseMonitor) { 
     if (form.HasBeenClosed) { 
      // maybe special code for this case 
     } 
     else { 
      cleanup(); 
      // and other code 
     } 
    } 
}; 

Form.FormClosing 이벤트는이 목적을 위해 잘 작동합니다 , 차이가 나는 경우 더 편리하게 사용할 수 있습니다.

이 코드가 작성된 방식에 따라 두 이벤트 처리기는 UI 스레드에서 실행되도록 예약됩니다 (이는 WinForms 구성 요소가 단일 스레드 아파트 모델을 사용하기 때문에 발생합니다). 따라서 실제적으로 경쟁에 영향을받지 않습니다 조건. 그러나 향후에 더 많은 스레드를 스폰하기로 결정하면 잠금을 사용하지 않는 한 경쟁 조건이 노출 될 수 있습니다. 실제로 나는 이것이 아주 자주 일어나는 것을 보았 기 때문에, 어쨌든 미래의 증거가되기 위해 동기화를 제안합니다. 동기화는 한 번만 발생하기 때문에 성능에 영향을 미치지 않습니다.

+0

제안에 감사드립니다. FormClosed 및 잠금을 사용합니다. 몇 가지 발언이 있습니다 : 1. 대신 FormClosing을 사용해야합니까? FormClosed에 대한 핸들러는 ** 폼이 닫히고 처리 된 후에 ** 실행될 수 있습니다. 2. FormClosed 또는 FormClosing을 사용한다고해서 양식을 삭제하는 다른 방법을 막을 수는 없습니다. 나는 명시 적으로 그것을 처분하지 않을거야,하지만 어쩌면 내 양식을 처분 할 수있는 다른 이유가 있을지 모르겠습니다. – Joh

+0

추가 설명 : 폐업에 대한 처분과 초점을 잊어 버린 경우 잠금 장치가 필요합니까? FormClosing 및 RunWorkerCompleted에 대한 핸들러는 모두 동일한 스레드에서 실행됩니다. – Joh

+0

양식은 분명히 표시되어있는 동안 폐기해서는 안되며, 어떤 이유로 든 코드에서 버그가 발생하면 런타임에서 등 뒤에서 처리하지 않습니다. 다른 질문의 경우에는 대답을 약간 확대했습니다. – Jon

관련 문제