安全销毁线程池 [英] Safely Destroying a Thread Pool

查看:131
本文介绍了安全销毁线程池的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下用C ++ 14编写的琐碎线程池的实现.

Consider the following implementation of a trivial thread pool written in C++14.

  • threadpool.h
  • threadpool.cpp

观察每个线程处于睡眠状态,直到被通知唤醒(或某些虚假的唤醒调用)为止,并且以下谓词的计算结果为true:

Observe that each thread is sleeping until it's been notified to awaken -- or some spurious wake up call -- and the following predicate evaluates to 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成员功能.

Furthermore, observe that a ThreadPool object uses the data member destroy_ to determine if its being destroyed -- the destructor has been called. Toggling this data member to true will notify each worker thread that it's time to finish its current task and any of the other queued tasks then synchronize with the thread that's destroying this object; in addition to prohibiting the enqueue member function.

为方便起见,析构函数的实现如下:

For your convenience, the implementation of the destructor is below:

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();
  }
}

Q:我不明白为什么在析构函数中将destroy_切换为true时,必须锁定对象的互斥锁.此外,仅需要设置其值还是访问其值?

Q: I do not understand why it's necessary to lock the object's mutex while toggling destroy_ to true in the destructor. Furthermore, is it only necessary for setting its value or is it also necessary for accessing its value?

BQ::可以在保持其原始用途的同时改进或优化此线程池实现吗?可以合并N数量的线程并将任务分配给它们并发执行的线程池?

BQ: Can this thread pool implementation be improved or optimized while maintaining it's original purpose; a thread pool that can pool N amount of threads and distribute tasks to them to be executed concurrently?

此线程池实现是从 Jakob Progsch的C ++ 11线程池存储库中分叉的,并提供了完整的代码逐步了解其实现的目的和一些主观的样式更改.

This thread pool implementation is forked from Jakob Progsch's C++11 thread pool repository with a thorough code step through to understand the purpose behind its implementation and some subjective style changes.

我正在向自己介绍并发编程,还有很多东西要学习-我是目前的新手并发程序员.如果我的问题用词不正确,请在您提供的答案中进行适当的更正.此外,如果答案可以针对首次被引入并行编程的客户,那将是最好的选择-对我自己和任何其他新手来说都是如此.

I am introducing myself to concurrent programming and there is still much to learn -- I am a novice concurrent programmer as it stands right now. If my questions are not worded correctly then please make the appropriate correction(s) in your provided answer. Moreover, if the answer can be geared towards a client who is being introduced to concurrent programming for the first time then that would be best -- for myself and any other novices as well.

推荐答案

如果ThreadPool对象的拥有线程是唯一以原子方式写入destroy_变量的线程,而工作线程仅以原子方式从destroy_变量,然后不,不需要互斥体来保护ThreadPool析构函数中的destroy_变量.通常,当必须执行原子操作集而无法通过平台上的单个原子指令来完成时,则互斥锁是必需的(即,超出原子交换的操作等).话虽这么说,线程池的作者可能试图在destroy_变量上强制某种类型的获取语义,而不恢复到原子操作(即内存围栏操作),和/或标志本身的设置不是被视为原子操作(取决于平台)...其他一些选项包括将变量声明为volatile以防止其被缓存等.您可以看到

If the owning thread of the ThreadPool object is the only thread that atomically writes to the destroy_ variable, and the worker threads only atomically read from the destroy_ variable, then no, a mutex is not needed to protect the destroy_ variable in the ThreadPool destructor. Typically a mutex is necessary when an atomic set of operations must take place that can't be accomplished through a single atomic instruction on a platform, (i.e., operations beyond an atomic swap, etc.). That being said, the author of the thread pool may be trying to force some type of acquire semantics on the destroy_ variable without restoring to atomic operations (i.e. a memory fence operation), and/or the setting of the flag itself is not considered an atomic operation (platform dependent)... Some other options include declaring the variable as volatile to prevent it from being cached, etc. You can see this thread for more info.

如果没有某种形式的同步操作,最坏的情况可能是由于destroy_变量被缓存在线程上而导致的工作程序无法完成.在内存排序模型较弱的平台上,如果允许存在良性内存竞争条件,则总是有可能...

Without some sort of synchronization operation in place, the worst case scenario could end up with a worker that won't complete due to the destroy_ variable being cached on a thread. On platforms with weaker memory ordering models, that's always a possibility if you allowed a benign memory race condition to exist ...

这篇关于安全销毁线程池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆