这与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();
}
问题是线程不会停止。
为什么?
-O0
和-O4
,但它没有任何区别。答案 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()
。