2010-03-09 4 views
11

C# 응용 프로그램이 닫히면 때때로 정리 루틴에 걸리게됩니다. 특히, 배경 작업자가 닫히지 않습니다. 이것은 내가 그것을 닫습니다 시도하고 어떻게 기본적으로 :백그라운드 작업 취소

개인 무효 App_FormClosing (개체를 보낸 사람, FormClosingEventArgs 전자) { backgroundWorker1.CancelAsync를(); while (backgroundWorker1.IsBusy); // 여기에 붙어 있습니다. }

이렇게해야하는 다른 방법이 있습니까? Microsoft Visual C# 2008 Express Edition을 사용하고 있습니다. 감사.

추가 정보 :

배경 노동자는 종료 될 나타나지 않습니다. 내가 일을해야 뭔가 다른

private void App_FormClosing(object sender, FormClosingEventArgs e) 
{ 
    while (backgroundWorker1.IsBusy) 
    { 
     backgroundWorker1.CancelAsync(); 
     System.Threading.Thread.Sleep(1000); 
    } 
} 

있습니까 :

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while (!backgroundWorker1.CancellationPending) 
    { 
     // Do something. 
    } 
} 

나는 또한 정리 코드를 수정했습니다 : 이것은 내가 가진 무엇인가?

+12

당신은 다음과 같이 회전 폴링 루프를 실행해서는 안됩니다, 그것은 CPU 사이클이 작업을 완료하려면 다른 스레드가 어려워 소비를 바랍니다. WaitHandle 또는 유사한 동기화 프리미티브를 사용할 수없고 절대적으로 폴링해야한다면 Thread.Sleep (x)를 거기에 추가하십시오. –

+0

+1을 @ Hightechrider 님의 의견입니다. 응용 프로그램이 종료되지 않을뿐만 아니라 잠자기 시간이 없으면 전체 시스템이 잠 깁니다. –

+0

경우에 따라서는 BackgroundWorker에 대해 CanBeCancel 또는 so 속성을 true로 설정 했습니까? –

답변

4

Kevin Gale이있는 BackgroundWorker의 DoWork 처리기 CancellationPending 폴링 및 취소가 요청 된 경우 반환해야한다는 올바른지.

즉, 응용 프로그램이 종료 될 때 이러한 상황이 발생하면 안전하게 무시할 수 있습니다. BackgroundWorker는 정의에 따라 백그라운드 스레드 인 ThreadPool 스레드를 사용합니다. 이 작업을 계속 실행해도 응용 프로그램이 종료되지 않으며 응용 프로그램이 종료 될 때 스레드가 자동으로 종료됩니다.

+1

+1 배경 작업자 스레드가 종료되기 전에 일부 유형의 정리를 수행해야하는 경우가 아니면 절대적입니다. –

+0

@ 케빈 : 사실,하지만 OP가 스레드를 취소하려고했기 때문에 그렇지 않은 것 같습니다. –

+0

감사합니다, 리드. 이것은 내가 필요로하는 것보다 훨씬 단순한 것을 단순하게 만드는 경우 인 것처럼 보입니다. –

4

백그라운드 작업자 스레드에서 BackgroundWorker.CancellationPending 플래그를 확인하고 true이면 종료해야합니다.

CancelAsync()는이 플래그를 설정합니다.

다른 방법으로 입력하십시오. CancelAsync()는 실제로 아무 것도 취소하지 않습니다. 스레드를 중단 시키거나 스레드를 종료하지 않습니다. 작업자 스레드가 루프에 있고 CancellationPending 플래그를 주기적으로 확인하면 취소 요청을 포착하여 종료 할 수 있습니다.

MSDN에는 작업자 루틴에서 루프를 사용하지 않지만 here이라는 예제가 있습니다.

+2

가깝지만 꽤 아닙니다. 당신의 배경 작업은 DoWorkEventArgs.Cancel' 속성을 검사해야합니다. 스레드간에 인스턴스 변수를 공유하지 않는다면, 백그라운드 작업은 백그라운드 Backgrounder의 실제 인스턴스에 액세스 할 수 없습니다. –

+0

Microsoft 고유의 예제는 sender 매개 변수를 통해 백그라운드 작업자에 액세스합니다. 그리고 어쨌든 나는 배경 작업자를위한 변수가 아니라 클래스의 속성을 참조했습니다. 좀 더 명확하게하기 위해 예제에 대한 링크를 추가했습니다. –

0

시도 :

if (this.backgroundWorker1.IsBusy) this.backgroundWorker1.CancelAsync(); 
1

이 코드는 BGW가 계속 실행 중일 때 교착 상태가 발생합니다. BGW는 RunWorkerCompleted 이벤트가 끝날 때까지 완료 할 수 없습니다. RunWorkerCompleted는 UI 스레드가 유휴 상태가되어 메시지 루프를 실행할 때까지 실행할 수 없습니다. 그러나 UI 스레드가 유휴 상태가 아니며 while 루프에서 멈 춥니 다.

BGW 스레드를 완전하게 완료하려면 양식을 유지하기 위해 이 있어야합니다. 방법을 확인하려면 this thread을 확인하십시오.

7

몇 가지 좋은 제안이 있지만 기본적인 문제를 해결할 수 있다고 생각하지 않습니다. 배경 작업을 취소하는 것입니다.

BackgroundWorker을 사용하는 경우 작업의 종료는 작업 자체에 따라 다릅니다. 사용자의 while 루프가 종료되는 유일한 방법은 백그라운드 작업이 Cancel 속성을 검사하고 현재 프로세스에서 복귀하거나 중단되는 경우입니다. 예를 들어 예 자료

, 이것은 기본적으로 무슨 일이 일어나고있다

private readonly BackgroundWorker worker = new BackgroundWorker(); 

public void SomeFormEventForStartingBackgroundTask() 
{ 
    worker.DoWork += BackgroundTask_HotelCalifornia; 
    worker.WorkerSupportsCancellation = true; 
    worker.RunWorkerAsync(); 
} 

// semantically, you want to perform this task for lifetime of 
// application, you may even expect that calling CancelAsync 
// will out and out abort this method - that is incorrect. 
// CancelAsync will only set DoWorkEventArgs.Cancel property 
// to true 
private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e) 
{ 
    for (; ;) 
    { 
     // because we never inspect e.Cancel, we can never leave! 
    } 
} 

private void App_FormClosing(object sender, FormClosingEventArgs e)  
{ 
    // [politely] request termination 
    worker.CancelAsync(); 

    // [politely] wait until background task terminates 
    while (worker.IsBusy); 
} 

고려하십시오. 자, 아마도 당신의 작업은 무한 루프가 아니며 아마도 장기 실행 작업 일 것입니다. 어느 쪽이든, 주 스레드는 작업이 완료 될 때까지 [실제로 회전하고 있지만 whatevs] 차단하거나 경우에 따라 그렇지 않습니다.

개인적으로 글을 쓰고 작업을 수정할 수있는 경우 몇 가지 옵션이 있습니다.

예 개선

는 예를 들어, 이것은이 더 있지만, 그것은 수만큼 좋지 않아 위의 예

private readonly BackgroundWorker worker = new BackgroundWorker(); 

// this is used to signal our main Gui thread that background 
// task has completed 
private readonly AutoResetEvent isWorkerStopped = 
    new AutoResentEvent (false); 

public void SomeFormEventForStartingBackgroundTask() 
{ 
    worker.DoWork += BackgroundTask_HotelCalifornia; 
    worker.RunWorkerCompleted += BackgroundTask_Completed; 
    worker.WorkerSupportsCancellation = true; 
    worker.RunWorkerAsync(); 
} 

private void BackgroundTask_HotelCalifornia (object sender, DoWorkEventArgs e) 
{ 
    // execute until canceled 
    for (; !e.Cancel;) 
    { 
     // keep in mind, this task will *block* main 
     // thread until cancel flag is checked again, 
     // so if you are, say crunching SETI numbers 
     // here for instance, you could still be blocking 
     // a long time. but long time is better than 
     // forever ;) 
    } 
} 

private void BackgroundTask_Completed (
    object sender, 
    RunWorkerCompletedEventArgs e) 
{ 
    // ok, our task has stopped, set signal to 'signaled' state 
    // we are complete! 
    isStopped.Set(); 
} 

private void App_FormClosing(object sender, FormClosingEventArgs e)  
{ 
    // [politely] request termination 
    worker.CancelAsync(); 

    // [politely] wait until background task terminates 
    isStopped.WaitOne(); 
} 

의 더 나은 구현입니다. 배경 작업이 끝날 것이라고 [합리적으로] 확신 할 수 있다면 "충분히 좋다"고 할 수 있습니다.

그러나, 우리는 [보통] 원하는, 우리는 할 수없는이

private void App_FormClosing(object sender, FormClosingEventArgs e)  
{ 
    // [politely] request termination 
    worker.CancelAsync(); 

    // [politely] wait until background task terminates 
    TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100); 
    bool isStoppedGracefully = isStopped.WaitOne (gracePeriod); 

    if (!isStoppedGracefully) 
    { 
     // KILL! KILL! KILL! 
    } 
} 

아아 같은 것입니다. BackgroundWorker은 강제 종료의 수단을 노출하지 않습니다. 이는 숨겨진 스레드 관리 시스템 위에 구축 된 추상화이기 때문에 강제 종료되는 경우 응용 프로그램의 다른 부분을 잠재적으로 비화시킬 수 있습니다.

위의 내용을 구현하는 유일한 방법은 자신의 스레딩을 관리하는 것입니다.

예 이상적인

그래서, 예를

private Thread worker = null; 

// this time, 'Thread' provides all synchronization 
// constructs required for main thread to synchronize 
// with background task. however, in the interest of 
// giving background task a chance to terminate gracefully 
// we supply it with this cancel signal 
private readonly AutoResetEvent isCanceled = new AutoResentEvent (false); 

public void SomeFormEventForStartingBackgroundTask() 
{ 
    worker = new Thread (BackgroundTask_HotelCalifornia); 
    worker.IsBackground = true; 
    worker.Name = "Some Background Task"; // always handy to name things! 
    worker.Start(); 
} 

private void BackgroundTask_HotelCalifornia() 
{ 
    // inspect cancel signal, no wait period 
    // 
    // NOTE: so cheating here a bit, this is an instance variable 
    // but could as easily be supplied via parameterized thread 
    // start delegate 
    for (; !isCanceled.WaitOne (0);) 
    { 
    } 
} 

private void App_FormClosing(object sender, FormClosingEventArgs e)  
{ 
    // [politely] request termination 
    isCanceled.Set(); 

    // [politely] wait until background task terminates 
    TimeSpan gracePeriod = TimeSpan.FromMilliseconds(100); 
    bool isStoppedGracefully = worker.Join (gracePeriod); 

    if (!isStoppedGracefully) 
    { 
     // wipe them out, all of them. 
     worker.Abort(); 
    } 
} 

그리고 거기에 대한, 스레드 관리에 괜찮은 소개합니다.

어떤 것이 가장 적합합니까? 귀하의 신청서에 따라 다릅니다. 그것은 보트 바위, 그리고

  1. 당신의 배경 작업이 검사 수 있도록 현재의 구현을 수정하는 것이 가장 하지하고 폴링
  2. 반대로, 완료의 Cancel 재산
  3. 주 스레드 대기를 존중

각 접근법의 장단점을 비교하고 평가하는 것이 매우 중요합니다.

경우 해야 다른 사람의 작업 제어 및 보증 종료 후 갈 수있는 방법이 될 수 있습니다 위의 통합 스레드 관리 시스템을 쓰기. 그러나 스레드 풀링, 진행률보고, 스레드 간 데이터 마샬링 [작업자는 ​​그럴 수 있습니까?] 및 기타 여러 가지 기능과 같은 기본 기능을 사용하지 못하게됩니다. 말할 것도없이, "자신 만의 롤링"은 종종 오류가 발생하기 쉽습니다. 어쨌든

이 도움이 :)

+0

'while '을 입력하는 것을 두려워하십니까? – martijnn2008