lock_guard 在 if-initializer 而不是简单作用域中初始化

lock_guard initialization in if-initializer instead of simple scope

这样说对吗

if (std::lock_guard lk(m_state.mtx); true) {
  m_state.aborting = true;
}

{
  std::lock_guard lk(m_state.mtx)
  m_state.aborting = true;
}

100% 相同?


这是我的问题的背景:在掌握C++17 STL中,作者给出了一个线程池的代码示例。它包含以下块(第 208 页):

~ThreadPool() {
  if (std::lock_guard lk(m_state.mtx); true) {  // <<--- Line of interest
    m_state.aborting = true;
  }

  m_cv.notify_all();
  for (std::thread& t : m_workers) { 
    t.join();
  }
}

标记的行使用 if 语句的初始化部分来创建 lock_guard。这已在 中使用,以便将 lock_guard 的范围限制在 if 语句中,同时保护 if 语句本身的评估。

在这个例子中让我感到困惑的是 if 语句没有合适的条件。根据 cppreference page on if,

If init-statement is used, the if statement is equivalent to

{
  init_statement
  if ( condition )
    statement-true
}

在这种情况下,这意味着 if (std::lock_guard lk(m_state.mtx); true) {...} 等同于

{
  std::lock_guard lk(m_state.mtx);
  if (true) {
    m_state.aborting = true;
  }
}

我是否遗漏了这不是 100% 相同的情况,或者它是用 if 语句编写的充分理由?

在下一页的不同示例中使用了相同的模式,所以我认为这是故意的,而不是实际情况下的遗留问题。

它们在语义上是相同的。由于这本书似乎都是关于新的 C++ 17 特性的,所以作者使用它是有道理的。但要小心:在尘埃落定之前,每个新功能都会被滥用,并且常识对使用该功能的好方法和坏方法达成共识。

以我的拙见,这种方式只会增加混乱(如果无条件执行该块,为什么会有 if 构造?)我更喜欢简单的块。时间会证明作者的方式是否会成为可接受的或不受欢迎的结构。或者,如果没有人会关心,或者它是否会永远存在争议。

我是 掌握 C++17 STL 的作者。

我已经忘记了那个结构,所以我使用它不可能有任何重要的原因。我相信伊戈尔是正确的:选择应该在

之间
~ThreadPool() {
  if (std::lock_guard lk(m_state.mtx); true) {
    m_state.aborting = true;
  }
  m_cv.notify_all();
  for (std::thread& t : m_workers) {
    t.join();
  }
}

以及诸如

之类的东西
~ThreadPool() {
  if (true) {
    std::lock_guard lk(m_state.mtx);
    m_state.aborting = true;
  }
  m_cv.notify_all();
  for (std::thread& t : m_workers) {
    t.join();
  }
}

它做同样的事情(可以说更清晰)但是在实体书中多了一行代码。

就我个人而言,我喜欢只写{来打开一个块;我总是宁愿写if (true) {。这有助于 reader 理解 if 并非意外遗漏(或合并冲突);程序员确实打算将块 运行 始终 。如果你像我(经常)那样一直抱住你所有的牙套,那么它也会保持不变,即你永远不会有一个 {} 单独坐在一条线上——不管它是什么价值

那里发生的另一件事是,它专门在通知条件变量的上下文中。我以前肯定写过

std::lock_guard lk(m_state.mtx);
m_state.aborting = true;
m_cv.notify_all();

notify 发生在锁的范围内;多年来,我经常听到“你应该在 通知之前释放锁”,以至于最终我也开始这么说(尽管我对 [=47= 仍然没有很好的直觉) ]为什么 应该是这样)。这实际上是我写这本书的五年前的第四个 SO 问题的主题:C++11 std::condition_variable: can we pass our lock directly to the notified thread?

所以我可能在编写这个特定片段时分心(思考不足)and/or 自我意识(思考过多),这可能导致我尝试这个特殊的奇怪结构。[​​=23=]

下一页的例子实际上是条件变量完全:

void enqueue_task(UniqueFunction task) {
  if (std::lock_guard lk(m_state.mtx); true) {
    m_state.work_queue.push(std::move(task));
  }
  m_cv.notify_one();
}

线程安全方面,这完全等同于

void enqueue_task(UniqueFunction task) {
  std::lock_guard lk(m_state.mtx);
  m_state.work_queue.push(std::move(task));
  m_cv.notify_one();
}

除了人们一直告诉我不要把通知放在锁里。因此 if (...; true) 版本勉强满足了这一流行的智慧,同时只花费一行代码而不是两行代码。

甚至有可能我最初是在锁定状态下通知的,然后这本书的一位技术审阅者说“不要在锁定状态下通知”,然后我做了这个最新的更改来专门处理这个问题最少的行数。我不记得这是否是真实的故事,但对我来说这很合理。