2015-06-24 83 views
0

我遇到以下問題,非常簡化的情況是我的項目的一部分。考慮到我們有GUI象下面這樣:Windows窗體:後臺工作人員同步和管理

Sample GUI

我有兩個背景工人:

  • plot_bgworker - 在這個例子中,它增加情節計數器,
  • data_bgworker - 在這個例子中,它增加數據計數器。

我也有label_timer,它會更新遞增值在我的窗體上顯示。

要同時管理背景的工人和定時器,我寫了兩個功能:

private: void turnOnAcquisition() { 
    if (!counting_paused) 
     return; 

    if (!plot_bgworker->IsBusy) 
     plot_bgworker->RunWorkerAsync(); 
    if (!data_bgworker->IsBusy) 
     data_bgworker->RunWorkerAsync(); 

    label_timer->Enabled = true; 
    counting_paused = false; 
} 

private: void turnOffAcquisition() { 

    if (counting_paused) 
     return; 

    if (plot_bgworker->IsBusy) 
     plot_bgworker->CancelAsync(); 
    if (data_bgworker->IsBusy) 
     data_bgworker->CancelAsync(); 

    label_timer->Enabled = false; 
    counting_paused = true; 
} 

然後,這裏是當我點擊我的每個按鈕會發生什麼:

// Pauses counting on click 
private: System::Void stop_btn_Click(System::Object^ sender, System::EventArgs^ e) { 
    turnOffAcquisition(); 
} 

// Starts counting on click 
private: System::Void start_btn_Click(System::Object^ sender, System::EventArgs^ e) { 
    turnOnAcquisition(); 
} 

// Should restart counting on click, beginning from 0 (no matter what state counting is in right now) 
private: System::Void restart_btn_Click(System::Object^ sender, System::EventArgs^ e) { 
    plot_counter = 0; 
    data_counter = 0; 
    turnOffAcquisition(); 
    turnOnAcquisition(); 
} 

最後,這裏是我的後臺工作人員(通過CancelAsync()/ RunWorkerAsync()關閉/打開)和計時器:

// Calculating data counter 
private: System::Void data_bgworker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { 
    for (;;) {   
     data_counter++; 
     Sleep(50); 
     if (data_bgworker->CancellationPending) { 
      e->Cancel = true; 
      return; 
     } 
    } 
} 

// Calculating plot counter 
private: System::Void plot_bgworker_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) { 
    for (;;) { 
     plot_counter++; 
     Sleep(120); 
     if (plot_bgworker->CancellationPending) { 
      e->Cancel = true; 
      return; 
     } 
    } 
} 

// Display counters 
private: System::Void label_timer_Tick(System::Object^ sender, System::EventArgs^ e) { 
    plot_counter_label->Text = numToMStr(plot_counter); 
    data_counter_label->Text = numToMStr(data_counter); 
} 

開始埠tton和停止按鈕都按預期工作,但現在我遇到了重啓按鈕的問題。當我在計數過程中單擊它時,它似乎重置值並停止後臺工作,但從不再次啓動它們(正如我在調用turnOnAcquisition之後所期望的那樣)。但是,當計數關閉時單擊它時,我可以按預期開啓計數。

我的第一個鏡頭是當我試圖檢查我的工作人員是否忙碌時,取消標記尚未設置爲另一個值,但在通話之間使用Sleep()並不起作用。另一個猜測是,它是由於競爭條件失敗,所以我嘗試使用MemoryBarrier(),但我不知道這些庫,我不知道它是否會工作。另外,我嘗試使用Interlocked類,但無法正確使用它作爲void函數。

1.這種思維方式是否正確?
2.如果是的話,爲什麼簡單的睡眠()不會做的伎倆?
3.在這種情況下,我將如何使用上述方法中的哪一種,哪一種方法最適合?

回答

0

好的,我自己找到了解決方案。這裏的問題是關於競爭條件 - 一個事件試圖停止計數(這意味着提出另一個事件),然後再次啓動(這是有問題的,因爲我的功能(我猜)已經與第一個和第二個事件甚至沒有被添加到事件檢測隊列中)。如果我解釋錯了,我會很感激那裏的一些批評;)

這裏有兩個修改的函數,它們正確地解決了線程管理。關鍵是讓其他事件能夠完成他們的工作直到我達到預期的狀態。

當我要關掉計數,我讓應用程序執行從隊列中的事件,直到兩個線程不會忙(的「而」循環):

private: void turnOffAcquisition() { 

    if (counting_paused) 
     return; 

    if (plot_bgworker->IsBusy) 
     plot_bgworker->CancelAsync(); 
    if (data_bgworker->IsBusy) 
     data_bgworker->CancelAsync(); 

    while((plot_bgworker->IsBusy) || (data_bgworker->IsBusy)) // Continue to process events until both workers stop working 
     Application::DoEvents();        // Then, you can process another thread requests! :) 

    label_timer->Enabled = false; 
    counting_paused = true; 
} 

相若方式,當我想重新開始計數,我讓應用程序執行這些事件,直到我檢查到兩個線程都忙(再次,'while'循環):

private: void turnOnAcquisition() { 
    if (!counting_paused) 
     return; 

    if (!plot_bgworker->IsBusy) 
     plot_bgworker->RunWorkerAsync(); 
    if (!data_bgworker->IsBusy) 
     data_bgworker->RunWorkerAsync(); 

    while((!plot_bgworker->IsBusy) || (!data_bgworker->IsBusy)) // Continue to process events until both workers start working 
     Application::DoEvents();        // Then, you can process another thread requests! :) 

    label_timer->Enabled = true; 
    counting_paused = false; 
}