我有一个启动多个工作线程的函数。每个工作线程都由一个对象封装,该对象的析构函数将尝试join
该线程,即调用if (thrd_.joinable()) thrd_.join();
。但事先不知道每个工人必须完成多少工作。管理功能使用互斥锁和条件变量将工作单元分配给线程。如果没有其他工作要做,则在保持互斥锁时设置某个标志,然后通知条件变量上阻塞的所有线程,以便它们唤醒,注意更改的标志并关闭。
即使主线程中存在异常,我希望此关闭也能正常工作。在Java中,我使用finally
子句来始终设置标志并在工作处理循环结束时通知线程。作为C++ doesn't have finally
,我写了自己的替代品:
class FinallyGuard {
private:
std::function<void()> f_;
public:
FinallyGuard(std::function<void()> f) : f_(f) { }
~FinallyGuard() { f_(); }
};
void Manager::manageWork(unsigned NumWorkers) {
// Order matters: destructors are called in reverse order and will
// 1. release the mutex lock so that workers can proceed
// 2. unblock all threads using the finally workalike
// 3. destroy the workers and join their threads
std::forward_list<Worker> workers;
FinallyGuard signalEndGuard([this] {
std::unique_lock<std::mutex> lk(mtx_);
done_ = true;
beginWork_.notify_all();
});
std::unique_lock<std::mutex> lk(mtx_);
for (unsigned i = 0; i != numWorkers; ++i)
workers.emplace_front(this);
while (haveMoreWork()) {
// …
}
}
但我在这里清楚地考虑其他语言的概念。 是否有更多类似C ++的方法来实现这一目标?解决方案要么需要执行一些代码,以便从方法中正常返回,也需要执行抛出异常的情况,或者提供更好的机制唤醒工人而不是旗帜和条件变量组合。
答案 0 :(得分:1)
尽管不是在核心语言中,但在C ++中确实存在等效的尝试。它被称为ScopeGuard,最初由Andrej Alexandrescu创造,他是“摇滚明星”中的一员。 C ++字段。在这里,他将在C ++和Beyond conference 2012 http://channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012-Andrei-Alexandrescu-Systematic-Error-Handling-in-C
上展示新版本更重要的是这里是代码: https://gist.github.com/KindDragon/4650442
你的例子几乎完全相同。如果您希望其他C ++程序员理解您的意思,您可能应该将其称为ScopeGuard或使用Alexandrescus代码(您可能将其置于库中或常用包含,经常使用)。 C ++程序员使用RAII作为一切,在我看来,表达意图非常重要,
SCOPE_EXIT {
std::unique_lock<std::mutex> lk(mtx_);
done_ = true;
beginWork_.notify_all();
};
在你的情况下。
我工作的地方ScopeGuard被认为是好的风格,并且通过代码审查就好了。如果您想在商业上使用它,它也是公共领域。
答案 1 :(得分:0)
C ++的方式是使用名为RAII的东西。您使用的事实是始终调用析构函数以确保始终运行某些代码。