0

我有一个启动多个工作线程的函数。每个工作线程都被一个对象封装,该对象的析构函数会尝试join对该线程进行调用,即调用if (thrd_.joinable()) thrd_.join();。但是每个工人必须完成多少工作是先验未知的。管理功能使用互斥锁和条件变量将工作单元分配给线程。如果没有更多工作要做,则在持有互斥锁时设置某个标志,然后通知所有在条件变量上阻塞的线程,以便它们醒来,注意更改的标志,然后关闭。

即使主线程中存在异常,我也希望此关闭工作。在 Java 中,我会使用一个finally子句来始终设置标志并在工作处理循环结束时通知线程。由于C++ 没有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++ 的方法来实现这一点?一个解决方案要么需要执行一些代码来从方法正常返回和抛出异常的情况,要么提供一些更好的机制来唤醒工作人员,而不是标志和条件变量的组合。

4

2 回答 2

1

C++ 中确实存在 try finally 等效项,但不在核心语言中。它被称为 ScopeGuard,最初由 Andrej Alexandrescu 创造,他是 C++ 领域的“摇滚明星”之一。在这里,他在 2012 年 C++ 和 Beyond 会议上介绍了新版本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 被认为是很好的风格,并且很好地通过了代码审查。如果您想在商业上使用它,它也是公共领域。

于 2013-03-05T17:00:29.337 回答
0

C++ 方法是使用称为RAII的东西。您使用总是调用析构函数的事实来确保始终运行某些代码。

于 2013-03-05T17:07:00.990 回答