CMake Release 在编译时评估 bool,而不是在执行期间
CMake Release evaluates bool when compiling, not during execution
我正在处理一个多线程项目。我正在使用 CMake 进行编译。我有一个 file/function 每隔一段时间就将 bool 设置为 true
#include <chrono>
void mainloop_click(int *cpm, bool *click, bool *end) {
auto start_time = std::chrono::system_clock::now();
while (!*end) {
*click = false;
while (std::chrono::duration<double>(std::chrono::system_clock::now() - start_time).count() < (60.0 / (double) *cpm));
*click = true;
start_time = std::chrono::system_clock::now();
}
}
我的测试函数有问题
void counter(int *count, bool *click, bool *end) {
while (!*end) {
if (*click) { // TODO Fix Release testing error. This always evaluates to false in release
(*count)++;
while (*click) {}
}
}
}
我测试的基本大纲是:
- 在自己的线程中启动 mainloop_click
- 在自己的线程中启动计数器,传递相同的点击指针。
- 测试计数器是否在设定的时间段后找到与预期的速度一样多的点击次数(根据
cpm
)。
据我所知,在 Debug 模式下,编译器实际上在可执行文件中有 if 语句求值,但当编译为 Release 可执行文件时,它不会自动求值 bool,click
,如false(因为它是线程启动之前的状态),实际上并没有检查它,但“知道”它是 false。有没有办法让 Release 不这样做?我添加了打印语句,并且知道测试中的第三行(带有 // TODO
)是问题发生的地方。
您的代码包含多个导致未定义行为的错误。我将在这个答案中强调两个可以解释您观察到的行为。
A bool
,和大多数对象一样,不能用于没有同步的线程间通信。您有两个线程,一个写入和读取同一个变量,即 data race。发生数据竞争意味着该程序具有未定义的行为。不允许您从另一个线程可能同时写入的对象中读取。
第二个错误是由于 progress guarantee。此保证使未定义行为具有无副作用的无限循环。
这个 UB 的一个可能结果是编译器可以看到一旦线程进入 counter
它永远不会改变 *click
,所以它可以假设 *click
在执行的持续时间。 bool
不允许在没有同步的情况下在线程之间共享,因此这是编译器部分的有效假设。
编译器还可以看到 while (*click) {}
没有副作用,因此它可以假设 *click
最终是 false
,因为循环可能不是无限的。
由于编译器可以假设 *click
是一个常量,并且 *click
最终必须是 false
,所以它可以假设 *click
总是 false
。
解决此问题的最简单方法是使用 std::atomic<bool>
而不是 bool
。这会以一种可在线程之间共享的方式包装 bool
。
但是,您似乎正在将 non-synchronized 对象用于许多其他 inter-thread 通信应用程序。您必须解决每一个问题。
多线程很难掌握,这不是应该通过反复试验来学习的东西。考虑获得关于该主题的 book。
我正在处理一个多线程项目。我正在使用 CMake 进行编译。我有一个 file/function 每隔一段时间就将 bool 设置为 true
#include <chrono>
void mainloop_click(int *cpm, bool *click, bool *end) {
auto start_time = std::chrono::system_clock::now();
while (!*end) {
*click = false;
while (std::chrono::duration<double>(std::chrono::system_clock::now() - start_time).count() < (60.0 / (double) *cpm));
*click = true;
start_time = std::chrono::system_clock::now();
}
}
我的测试函数有问题
void counter(int *count, bool *click, bool *end) {
while (!*end) {
if (*click) { // TODO Fix Release testing error. This always evaluates to false in release
(*count)++;
while (*click) {}
}
}
}
我测试的基本大纲是:
- 在自己的线程中启动 mainloop_click
- 在自己的线程中启动计数器,传递相同的点击指针。
- 测试计数器是否在设定的时间段后找到与预期的速度一样多的点击次数(根据
cpm
)。
据我所知,在 Debug 模式下,编译器实际上在可执行文件中有 if 语句求值,但当编译为 Release 可执行文件时,它不会自动求值 bool,click
,如false(因为它是线程启动之前的状态),实际上并没有检查它,但“知道”它是 false。有没有办法让 Release 不这样做?我添加了打印语句,并且知道测试中的第三行(带有 // TODO
)是问题发生的地方。
您的代码包含多个导致未定义行为的错误。我将在这个答案中强调两个可以解释您观察到的行为。
A bool
,和大多数对象一样,不能用于没有同步的线程间通信。您有两个线程,一个写入和读取同一个变量,即 data race。发生数据竞争意味着该程序具有未定义的行为。不允许您从另一个线程可能同时写入的对象中读取。
第二个错误是由于 progress guarantee。此保证使未定义行为具有无副作用的无限循环。
这个 UB 的一个可能结果是编译器可以看到一旦线程进入 counter
它永远不会改变 *click
,所以它可以假设 *click
在执行的持续时间。 bool
不允许在没有同步的情况下在线程之间共享,因此这是编译器部分的有效假设。
编译器还可以看到 while (*click) {}
没有副作用,因此它可以假设 *click
最终是 false
,因为循环可能不是无限的。
由于编译器可以假设 *click
是一个常量,并且 *click
最终必须是 false
,所以它可以假设 *click
总是 false
。
解决此问题的最简单方法是使用 std::atomic<bool>
而不是 bool
。这会以一种可在线程之间共享的方式包装 bool
。
但是,您似乎正在将 non-synchronized 对象用于许多其他 inter-thread 通信应用程序。您必须解决每一个问题。
多线程很难掌握,这不是应该通过反复试验来学习的东西。考虑获得关于该主题的 book。