簡體   English   中英

安全地銷毀線程池

[英]Safely Destroying a Thread Pool

考慮以C ++ 14編寫的普通線程池的以下實現。

觀察每個線程正在休眠,直到它被通知喚醒 - 或者一些虛假的喚醒調用 - 並且以下謂詞評估為true

std::unique_lock<mutex> lock(this->instance_mutex_);

this->cond_handle_task_.wait(lock, [this] {
  return (this->destroy_ || !this->tasks_.empty());
});

此外,觀察ThreadPool對象使用數據成員destroy_來確定它是否被銷毀 - 已經調用了析構函數。 將此數據成員切換為true將通知每個工作線程是時候完成其當前任務,然后任何其他排隊任務與正在銷毀此對象的線程同步; 除了禁止入enqueue成員的功能。

為方便起見,析構函數的實現如下:

ThreadPool::~ThreadPool() {
  {
    std::lock_guard<mutex> lock(this->instance_mutex_); // this line.

    this->destroy_ = true;
  }

  this->cond_handle_task_.notify_all();

  for (auto &worker : this->workers_) {
    worker.join();
  }
}

問:我不明白為什么在析構函數destroy_切換為true時需要鎖定對象的互斥鎖。 此外,是否只需要設置其值或是否也需要訪問其值?

BQ:可以在保持最初目的的同時改進或優化此線程池實現; 一個線程池,可以匯集N個線程並將任務分配給它們以便同時執行?


這個線程池實現是從Jakob Progsch的C ++ 11線程池存儲庫中分離出來的,其中包含一個完整的代碼步驟,以了解其實現背后的目的和一些主觀樣式更改。

我將自己介紹給並發編程,還有很多東西需要學習 - 我現在是一名新手並發程序員。 如果我的問題措辭不正確,請在您提供的答案中進行適當的更正。 此外,如果答案可以面向首次引入並發編程的客戶,那么這對我自己和任何其他新手來說都是最好的。

如果ThreadPool對象的擁有線程是唯一以原子方式寫入destroy_變量的線程,並且工作線程只是從destroy_變量中原子讀取,那么不需要一個互斥鎖來保護ThreadPool析構函數中的destroy_變量。 通常,當必須發生無法通過平台上的單個原子指令完成的原子操作集(即,超出原子交換的操作等)時,互斥是必要的。 話雖這么說,線程池的作者可能試圖在destroy_變量上強制某種類型的獲取語義而不恢復原子操作(即內存柵欄操作),和/或標志本身的設置不被認為是原子操作(依賴於平台)...其他一些選項包括將變量聲明為volatile以防止它被緩存等。您可以看到此線程以獲取更多信息。

如果沒有某種類型的同步操作,最壞的情況可能會導致由於destroy_變量緩存在線程上而無法完成的工作程序。 在具有較弱內存排序模型的平台上,如果您允許存在良性內存競爭條件,則總是有可能...

C ++將數據爭用定義為可能同時訪問對象的多個線程,其中至少一個訪問是寫入。 具有數據爭用的程序具有未定義的行為。 如果你在沒有持有互斥鎖的情況下寫你的析構函數中的destroy ,你的程序將會有未定義的行為,我們無法預測會發生什么。

如果你在沒有持有互斥鎖的情況下閱讀其他地方的destroy ,那么當析構函數寫入它時也可能發生這種讀取,這也是數據競爭。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM