문제

컨트롤러를 사용하여 실제 DAL 호출을 수행하는 데이터베이스 페이징에 대한 간단한 usercontrol이 있습니다. 나는 a를 사용한다 BackgroundWorker 무거운 리프팅을 수행하고 OnWorkCompleted 이벤트 일부 버튼을 다시 활성화하고 변경합니다 TextBox.Text 부모 양식을위한 재산 및 이벤트를 제기합니다.

양식 A는 내 우르 컨트롤을 유지합니다. 양식 B를 열는 버튼을 클릭하면 "아무것도하지 않고"닫고 데이터베이스에서 다음 페이지를 가져 오려고 시도합니다. OnWorkCompleted 작업자 스레드 (내 메인 스레드가 아님)에서 호출되고 교차 스레드 예외를 던집니다.

현재 수표를 추가했습니다 InvokeRequired 거기 핸들러에서는 OnWorkCompleted 메인 스레드에서 호출 될까요? 왜 예상대로 작동하지 않습니까?

편집하다:

나는 문제를 Arcgis로 좁힐 수 있었고 BackgroundWorker. 다음 솔루션이 있습니다. Form1 두 개의 버튼으로.

첫 번째 버튼은 a BackgroundWorker 그것은 500ms를 위해 자고 카운터를 업데이트합니다. 에서 RunWorkerCompleted 방법을 확인합니다 InvokeRequired, 및 메소드가 메인 스레드 또는 작업자 스레드 내부에서 실행되는 메소드가 어디에 있는지 표시하기 위해 제목을 업데이트합니다. 두 번째 버튼이 열립니다 Form2, 아무것도 포함하지 않습니다.

처음에는 모든 전화가 있습니다 RunWorkerCompletedare 메인 스레드 내부에서 만들어집니다 (예상대로 - 런 워크 커피 방법의 Whold 지점은 적어도 내가 MSDN ~에 BackgroundWorker)

열고 닫은 후 Form2,, RunWorkerCompleted 작업자 실에서 항상 호출되고 있습니다. 이 솔루션이 문제에 대해이 솔루션을 그대로 남길 수 있다고 덧붙이고 싶습니다 (확인하십시오. InvokeRequired 에서 RunWorkerCompleted 방법), 그러나 나는 그것이 내 기대에 반대하는 이유를 이해하고 싶습니다. 내 "실제"코드에서 나는 항상 RunWorkerCompleted 기본 스레드에서 메소드가 호출되고 있습니다.

나는 문제를 지적했다 form.Show(); 내 명령 BackgroundTesterBtn - 내가 사용하는 경우 ShowDialog() 대신, 나는 아무런 문제가 없다 (RunWorkerCompleted 항상 메인 스레드에서 실행됩니다). 사용해야합니다 Show() 내 ARCMAP 프로젝트에서 사용자가 양식에 구속되지 않도록합니다.

또한 일반 WinForms 프로젝트에서 버그를 재현하려고했습니다. ArcMap없이 첫 번째 양식을 열는 간단한 프로젝트를 추가했지만이 경우 버그를 재현 할 수 없었습니다. RunWorkerCompleted 내가 사용하든 메인 스레드에서 달렸다 Show() 또는 ShowDialog(), 개방 전후 Form2. 나는주기 전에 주요 형식으로 작용하기 위해 세 번째 양식을 추가하려고 시도했습니다. Form1, 그러나 결과는 바뀌지 않았습니다.

여기 내 간단한 SLN (vs2005sp1)입니다.

esri.arcgis.adf (9.2.4.1420)

esri.arcgis.arcmapui (9.2.3.1380)

esri.arcgis.systemui (9.2.3.1380)

도움이 되었습니까?

해결책

버그처럼 보입니다.

http://connect.microsoft.com/visualstudio/feedback/viewfeedback.aspx?feedbackid=116930

http://thedatafarm.com/devlifeblog/archive/2005/12/21/39532.aspx

따라서 방탄 (pseudocode)을 사용하는 것이 좋습니다.

if(control.InvokeRequired)
  control.Invoke(Action);
else
  Action()

다른 팁

요점이 아닙니다 OnWorkCompleted 메인 스레드에서 호출 될까요? 왜 예상대로 작동하지 않습니까?

아니요, 그렇지 않습니다.
오래된 스레드에서 오래된 것을 실행할 수는 없습니다. 스레드는 단순히 "이 실행, 제발"이라고 말할 수있는 예의 바른 개체가 아닙니다.

실의 더 나은 정신 모델은화물 열차입니다. 일단 가면 자체 트랙에서 벗어납니다. 코스를 변경하거나 중지 할 수 없습니다. 영향을 미치고 싶다면 다음 기차역에 도착할 때까지 기다려야합니다 (예 : 일부 이벤트를 수동으로 확인하십시오).Thread.Abort 그리고 Crossthread 예외는 기차를 탈선시키는 것과 거의 같은 결과를 가져옵니다 ...주의하십시오!).

winforms 컨트롤 일종의 이 행동을 지원합니다 (그들은 가지고 있습니다 Control.BeginInvoke UI 스레드에서 기능을 실행할 수 있지만 Windows UI 메시지 펌프에 특수 고리가 있고 특수 처리기를 작성하기 때문에 작동합니다. 위의 비유를 사용하기 위해, 그들의 열차는 역에서 체크인하고 주기적으로 새로운 방향을 찾아 해당 시설을 사용하여 자신의 지시를 게시 할 수 있습니다.

그만큼 BackgroundWorker Windows를 사용할 수 없으므로 일반적인 목적 (Windows GUI에 연결할 수 없음)으로 설계되었습니다. Control.BeginInvoke 특징. 메인 스레드는 막을 수없는 '열차'라고 가정해야하므로 완성 된 이벤트는 작업자 스레드에서 실행되어야합니다.

그러나 Winforms를 사용하면서 OnWorkCompleted 핸들러, 창을 실행할 수 있습니다 또 다른 콜백을 사용합니다 BeginInvoke 내가 위에서 언급 한 기능. 이와 같이:

// Assume we're running in a windows forms button click so we have access to the 
// form object in the "this" variable.
void OnButton_Click(object sender, EventArgs e )
    var b = new BackgroundWorker();
    b.DoWork += ... blah blah

    // attach an anonymous function to the completed event.
    // when this function fires in the worker thread, it will ask the form (this)
    // to execute the WorkCompleteCallback on the UI thread.
    // when the form has some spare time, it will run your function, and 
    // you can do all the stuff that you want
    b.RunWorkerCompleted += (s, e) { this.BeginInvoke(WorkCompleteCallback); }
    b.RunWorkerAsync(); // GO!
}

void WorkCompleteCallback()
{
    Button.Enabled = false;
    //other stuff that only works in the UI thread
}

또한 이것을 잊지 마십시오 :

RunworkerCompleted 이벤트 핸들러는 결과 속성에 액세스하기 전에 항상 오류 및 취소 속성을 확인해야합니다. 예외가 발생하거나 작업이 취소 된 경우 결과 속성에 액세스하면 예외가 발생합니다.

그만큼 BackgroundWorker 대의원 인스턴스가 인터페이스를 지원하는 클래스를 가리키는 지 확인합니다. ISynchronizeInvoke. DAL 레이어는 아마도 해당 인터페이스를 구현하지 않을 것입니다. 일반적으로 사용합니다 BackgroundWorker a Form, 해당 인터페이스를 지원합니다.

사용하려는 경우 BackgroundWorker DAL 레이어에서 UI를 업데이트하려면 세 가지 옵션이 있습니다.

  • 당신은 계속 전화 할 것입니다 Invoke 방법
  • 인터페이스를 구현하십시오 ISynchronizeInvoke DAL 클래스에서 호출을 수동으로 리디렉션합니다 (세 가지 방법과 속성입니다)
  • 호출하기 전에 BackgroundWorker (그래서 UI 스레드에서) SynchronizationContext.Current 인스턴스 변수에 컨텐츠 인스턴스를 저장합니다. 그만큼 SynchronizationContext 그러면 당신에게 Send 방법, 정확히 무엇을 할 것입니다 Invoke 하다.

GUI의 교차 스레딩 문제를 피하기위한 최선의 방법은 SynchronizationContext를 사용하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top