使用`atomic_flag`变量停止线程

时间:2017-06-14 16:38:29

标签: multithreading c++11

这与this问题的第二个答案有关。

我的测试代码如下。我正在尝试启动一个线程,然后让它停止使用std::atomic_flag。然后线程应该输出一些循环执行和总持续时间,然后停止。

std::atomic_flag keepRunning = ATOMIC_FLAG_INIT;

void F()
{
  keepRunning.test_and_set();
  long long unsigned count = 0;
  const time_t start = time(nullptr);
  while (keepRunning.test_and_set())
  {
    std::cout << '.';
    ++count;
  }
  const time_t duration = time(nullptr) - start;
  std::cout << count << '\t' << duration << std::endl;
}

int main()
{
  std::thread t(F);
  keepRunning.clear();
  t.join();
}

问题是线程不会停止

为什么?

  • 编译器:g ++(Ubuntu 4.8.4-2ubuntu1~14.04.3)4.8.4
  • 操作系统:macOS Sierra Ver 10.12.2主机上的客户操作系统Ubuntu 14.04
  • 编辑标记 - 我尝试了-O0-O4,但它没有任何区别。

2 个答案:

答案 0 :(得分:1)

它不会停止(大部分时间)因为F中的循环可能永远不会看到清除标志。

假设创建thread需要一些时间,keepRunning.clear()中的main可能会先运行。 当F最终运行时,它会立即设置该值并进入一个永远不会看到已清除标志的循环,因此永远不会退出。

解决方案是将其初始化为F,而不是最初在true中设置标记值。但是,std::atomic_flag不允许您这样做,因为它的接口有限(此设计是故意的,std::atomic_flag应该用作其他基元的低级构建块)。

您可以使用std::atomic<bool>,将其初始化为true并移除store(true)中的初始F。出于演示目的,我在清除sleep_for中的标记之前添加了main语句。

std::atomic<bool> keepRunning{true};

void F()
{
  long long unsigned count = 0;
  const time_t start = time(nullptr);
  while (keepRunning)
  {
    std::cout << '.';
    ++count;
  }
  const time_t duration = time(nullptr) - start;
  std::cout << count << '\t' << duration << std::endl;
}

int main()
{
  std::thread t(F);
  std::this_thread::sleep_for(1s); // optional
  keepRunning = false;
  t.join();
}

答案 1 :(得分:1)

F()中,您忽略了第一个keepRunning.test_and_set()的输出。您尝试初始化该标志会导致在keepRunning.clear()中使用main()语句进行竞争。根据这些语句中的哪一个首先运行,您要么获得预期的行为,要么忽略clear()调用和永不停止的线程。

F()有机会运行时,标志应该已经用正确的值初始化。将初始test_and_set()移至main()会阻止竞赛:

std::atomic_flag keepRunning = ATOMIC_FLAG_INIT;

void F()
{
  long long unsigned count = 0;
  const time_t start = time(nullptr);
  while (keepRunning.test_and_set())
  {
    std::cout << '.';
    ++count;
  }
  const time_t duration = time(nullptr) - start;
  std::cout << count << '\t' << duration << std::endl;
}

int main()
{
  keepRunning.test_and_set();
  std::thread t(F);
  keepRunning.clear();
  t.join();
}

现在该标志实际上只在main()中被更改,并且只有&#34; read&#34;在F()