2012-07-10 2 views
1

응용 프로그램의 수명 동안 실행되는 백그라운드 스레드가있는 GUI 응용 프로그램이 있다고 가정합니다. 응용 프로그램을 닫을 때 이러한 백그라운드 스레드를 깨끗하게 닫고 싶습니다. 실제로는 GUI를 끊지 않고 데이터 수집 및 처리 작업을 수행하기 위해 몇 개의 스레드가 실행되는 경우가 종종 있습니다.application :: doevents를 호출하지 않고 백그라운드 작업자 스레드를 종료하려면 어떻게해야합니까?

아래 예제에서는 문제를 보여줍니다. 즉, 백그라운드 작업자를 취소하면 주 스레드에서 작업자 완료 메소드가 호출됩니다. Application::DoEvents()에 대한 호출이 없으면 코드는 무기한으로 중지되지만, 전에 DoEvents을 호출하는 문제가 발생했습니다. 내 직감은 나쁘다고합니다.

질문은 다음과 같습니다. 내 응용 프로그램이 종료되면 백그라운드 작업자 스레드를 완전히 종료하는 올바른 방법은 무엇입니까? BackgroundWorker에이 RunWorkerCompleted 이벤트가 발생하면

using namespace System; 
using namespace System::ComponentModel; 
using namespace System::Collections; 
using namespace System::Windows::Forms; 
using namespace System::Data; 
using namespace System::Drawing; 

/// <summary> 
/// Summary for Form1 
/// 
/// WARNING: If you change the name of this class, you will need to change the 
///   'Resource File Name' property for the managed resource compiler tool 
///   associated with all .resx files this class depends on. Otherwise, 
///   the designers will not be able to interact properly with localized 
///   resources associated with this form. 
/// </summary> 
public ref class Form1 : public System::Windows::Forms::Form 
{ 
private: System::ComponentModel::BackgroundWorker^ backgroundWorker1; 

public: 
    Form1(void) 
    { 
     InitializeComponent(); 

     // 
     // backgroundWorker1 
     // 
     this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker()); 
     this->backgroundWorker1->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &Form1::backgroundWorker1_DoWork); 
     this->backgroundWorker1->RunWorkerCompleted += gcnew System::ComponentModel::RunWorkerCompletedEventHandler(this, &Form1::backgroundWorker1_RunWorkerCompleted); 
     this->backgroundWorker1->ProgressChanged += gcnew System::ComponentModel::ProgressChangedEventHandler(this, &Form1::backgroundWorker1_ProgressChanged); 
     this->backgroundWorker1->WorkerReportsProgress = true; 
     this->backgroundWorker1->WorkerSupportsCancellation = true; 

     // 
     //TODO: Add the constructor code here 
     // 
     backgroundWorker1->RunWorkerAsync(); 
    } 

protected: 
    /// <summary> 
    /// Clean up any resources being used. 
    /// </summary> 
    ~Form1() 
    { 
     if(backgroundWorker1 != nullptr) 
     { 
      backgroundWorker1->CancelAsync(); 
     } 

     while(backgroundWorker1->IsBusy == true) 
     { 
      System::Diagnostics::Debug::WriteLine("Waiting for background worker to exit.."); 
      System::Threading::Thread::Sleep(1000); 
      // Application::DoEvents(); <-- Don't want to do this but what are the alternatives? 
     } 

     System::Diagnostics::Debug::WriteLine("Form1 destructor complete!"); 
    } 

private: System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { 
      while(backgroundWorker1->CancellationPending == false) 
      { 
       System::Diagnostics::Debug::WriteLine("Working.."); 
       System::Threading::Thread::Sleep(1000); 
      } 
     } 

private: System::Void backgroundWorker1_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e) { 
     } 

private: System::Void backgroundWorker1_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e) 
     { 
      System::Diagnostics::Debug::WriteLine("Exiting.."); 
      System::Threading::Thread::Sleep(1000); 

     } 

private: 
    /// <summary> 
    /// Required designer variable. 
    /// </summary> 
    System::ComponentModel::Container ^components; 

#pragma region Windows Form Designer generated code 
    /// <summary> 
    /// Required method for Designer support - do not modify 
    /// the contents of this method with the code editor. 
    /// </summary> 
    void InitializeComponent(void) 
    { 
     this->SuspendLayout(); 
     // 
     // Form1 
     // 
     this->AutoScaleDimensions = System::Drawing::SizeF(6, 13); 
     this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; 
     this->ClientSize = System::Drawing::Size(443, 343); 
     this->Name = L"Form1"; 
     this->Text = L"Form1"; 
     this->ResumeLayout(false); 

    } 
#pragma endregion 

}; 
+0

가능한 해결책을 찾았습니다. http://social.msdn.microsoft.com/Forums/nl/csharpgeneral/thread/14679d00-3d46-4de4-be87-e66c196f0638 –

+0

[가능한 중복] (http : // stackoverflow .com/questions/4732737/how-to-stop-backgroundworker- 정확하게)'DoEvents'의 사용을지지하는 한 답변 (http://stackoverflow.com/a/7625188/15369) ... –

+0

DoEvents에 대한 [일부 흥미로운 배경 정보] (http://blogs.msdn.com/b/jfoscoding/archive/2005/08/06/448560.aspx) ... –

답변

1

, 그것은 소멸자가 실행중인 하나 인 메인 GUI 스레드에 그렇게. DoEvents를 호출하면 프레임 워크는 RunWorkerCompleted 이벤트를 포함하여 해당 스레드에서 실행 대기중인 다른 작업을 실행할 수 있습니다. 나는 좋겠)

Starting background worker from thread 1 
Working on thread 3.. 
Working on thread 3.. 
Waiting on thread 1 for background worker to exit.. 
Exiting on thread 1.. 
Form1 destructor complete! 

그래서, 당신은 응용 프로그램 :: DoEvents를 (사용하지 않는 경우 :

나는 디버그 메시지 Thread::CurrentThread->ManagedThreadId (및 주석 Application::DoEvents())를 추가하고, 여기에 내가 가진 무엇 다른 메커니즘을 사용하여 작업자가 종료 한 UI 스레드에 알립니다. ManualResetEvent는 여기에서 트릭을 멋지게 할 것입니다.

private: 
    ManualResetEvent^ mre; 

public: 
    Form1(void) 
    { 
     InitializeComponent(); 

     this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker()); 
     this->backgroundWorker1->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &Form1::backgroundWorker1_DoWork); 
     // Don't use this->backgroundWorker1->RunWorkerCompleted 
     this->backgroundWorker1->ProgressChanged += gcnew System::ComponentModel::ProgressChangedEventHandler(this, &Form1::backgroundWorker1_ProgressChanged); 
     this->backgroundWorker1->WorkerReportsProgress = true; 
     this->backgroundWorker1->WorkerSupportsCancellation = true; 

     this->mre = gcnew ManualResetEvent(false); 

     System::Diagnostics::Debug::WriteLine("Starting background worker from thread {0}", Thread::CurrentThread->ManagedThreadId); 
     backgroundWorker1->RunWorkerAsync(); 
    } 

protected: 
    ~Form1() 
    { 
     if(backgroundWorker1 != nullptr) 
     { 
      backgroundWorker1->CancelAsync(); 
     } 

     System::Diagnostics::Debug::WriteLine("Waiting on thread {0} for background worker to exit..", Thread::CurrentThread->ManagedThreadId); 
     this->mre->WaitOne(); 

     System::Diagnostics::Debug::WriteLine("Form1 destructor complete!"); 
    } 

private: 
    System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) 
     { 
      try 
      { 
       while(backgroundWorker1->CancellationPending == false) 
       { 
        System::Diagnostics::Debug::WriteLine("Working on thread {0}..", Thread::CurrentThread->ManagedThreadId); 
        System::Threading::Thread::Sleep(1000); 
       } 
      } 
      finally 
      { 
       this->mre->Set(); 
      } 
     } 

이제는 BackgroundWorker가 아닌 일반 스레드를 사용하는 것이 좋습니다. UI 스레드에서 '완료된'이벤트와 '진행'이벤트를 실행하여 UI 스레드에서 직접 수행하기에는 너무 긴 작업을 수행하지만 몇 초 후에 결과가 UI에 표시됩니다. (UI 스레드에서 이벤트를 실행하면 작업 결과로 UI 컨트롤을 쉽게 업데이트 할 수 있습니다.) 여기에 표시된 내용은 폼이 열려있는 전체 시간 동안 실행되는 것으로, BackgroundWorker를 활용하면 스레드를 만들고 직접 처리 할 수 ​​있습니다.