【问题标题】:Mistake in cpp specification about mutex try_lock?cpp规范中关于互斥锁try_lock的错误?
【发布时间】:2015-01-17 22:40:51
【问题描述】:

在后续链接 (http://www.cplusplus.com/reference/mutex/mutex/try_lock/) 上,我们声明样本只能返回 1 到 100000 之间的值。是否声明 0 不能在输出中?

// mutex::try_lock example
#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <mutex>          // std::mutex

volatile int counter (0); // non-atomic counter
std::mutex mtx;           // locks access to counter

void attempt_10k_increases () {
  for (int i=0; i<10000; ++i) {
    if (mtx.try_lock()) {   // only increase if currently not locked:
      ++counter;
      mtx.unlock();
    }
  }
}

int main ()
{
  std::thread threads[10];
  // spawn 10 threads:
  for (int i=0; i<10; ++i)
    threads[i] = std::thread(attempt_10k_increases);

  for (auto& th : threads) th.join();
  std::cout << counter << " successful increases of the counter.\n";

  return 0;
}

无论如何,回答“如何获得 2?”很容易,但真的不清楚如何获得 1 而永远不会获得 0。

try_lock 可以“当没有其他线程对互斥锁拥有锁时虚假地失败,但在这种情况下重复调用会在某个时候成功”,但如果它是真的,那么 sample 可以返回 0(也可以在某些情况)。

但是,如果这个规范样本声明为真并且 0 不能在输出中,那么关于“虚假失败”的词可能不是真的吗?

【问题讨论】:

  • 你会称 10000 个电话为“重复电话”吗?
  • 啊,the joys of cplusplus.com。虽然如果你是迂腐的,该网站并没有声称 0 是不可能的。它只是声称 1-100000 是可能的。
  • 我会说这是全球进步保证的边缘案例之一,很难从语言中正式化,但在某种程度上假设在任何合理的平台上,try_lock 的失败暗示 some 其他线程在某个时候取得了进展。
  • @kerrek-sb 为什么站点不声明 [0..100000] ?这是否意味着“虚假失败”可能会发生,但不会发生在单个第一个初始 try_lock 中?
  • @aefimov:我不知道网站为什么这么说,不是我写的。正如我所说,我认为不存在任何真正的硬件,其 0 是实际结果,但我也不认为标准可以保证这一点。

标签: c++


【解决方案1】:

标准规定如下:

30.4.1.2/14 [thread.mutex.requirements.mutex]

一个实现 即使它没有被任何其他线程持有,也可能无法获得锁。 [注意:这种虚假故障是 通常不常见,但允许基于简单比较和交换的有趣实现 (第 29 条)。 ——尾注]

所以如果try_lock 全部失败,你甚至可以得到0

另外,请不要使用cplusplus.com,它有一个long history 有很多错误。
使用标准的much closer cppreference.com 会更安全

【讨论】:

【解决方案2】:

例如,如果另一个线程持有一个锁并刚刚释放它,try_lock 可能会失败。您读到“在这些情况下的重复调用将在某个时候成功”。对 try_lock 进行 10,000 次调用将计为“重复调用”,其中一个会成功。

【讨论】:

  • 这意味着你也可以得到 0,但如果我们尊重 cplusplus.com 上的单词,它就被宣布为不可能(仅声明值 [1..100000]):)
【解决方案3】:

你永远不可能得到 0:

当第一次调用try_lock() 时(不管哪个线程先在这里),互斥锁被解锁。这意味着 10 个线程中的 1 个将设法锁定互斥锁,这意味着 try_lock() 将成功。

你可以得到1:

  • 假设线程 0 设法锁定了互斥锁:

void attempt_10k_increases () { for (int i=0; i<10000; ++i) { if (mtx.try_lock()) { // thread 1 to 9 are here ++counter; // thread 0 is here mtx.unlock(); } } }

  • 现在我们说操作系统调度程序选择暂时不运行线程 0。与此同时,线程 1 到 9 继续运行,调用 try_lock() 并失败,因为线程 0 持有互斥锁。

    • 线程 1 到 9 现已完成。他们甚至一次都没有获得互斥锁。
    • 线程 0 被重新调度。它解锁互斥锁并完成。
    • 计数器现在是 1。

【讨论】:

  • 要做到这一点,您需要所有线程 10000 次 try_lock 失败,一个线程失败 9999 次并且最后一次调用成功。
  • @aefimov 那么第一次调用很有可能成功。 “当没有其他线程锁定互斥体时,此函数可能会虚假失败,但在这些情况下重复调用会在某个时候成功。”我想我的例子有点愚蠢,但我认为“在某些时候”在我的例子中第一次出现在 thread0 unlock() 之后。不过这种可能性不大。而且我认为所有线程不可能在 10k 调用中同时失败。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-10
  • 2012-04-20
  • 2015-10-06
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多