以下代码是多线程增量计数器和打印的良好解决方案吗?

Is the following code a good solution for a multi-threaded increment counter and print?

请问您有什么看法吗? 你将做点什么不同的? 我的意思是,你认为如果我用 std::task 或 std::mutex、std::condition_variable 等来做会更好吗? 我用 2 个标志控制线程太过分了?

std::atomic<int> counter = { 0 };
std::atomic<bool> switchFlag = { false };
std::atomic<bool> finished = { false };
constexpr int MAX_NUM = 10;

void increment(){
    while (!finished.load()){
        if (!switchFlag.load()){
            std::cout << "incremented to =" << ++counter << '\n';
            switchFlag.store(true);
        }
    }
}

void print(){
    while (!finished.load()) {
        if (switchFlag.load()){
            std::cout << "counter=" << counter.load() << '\n';
            if (counter.load() >= MAX_NUM)
                finished.store(true);

            switchFlag.store(false);
        }
    }
}

int main() {
    auto t1 = std::thread(increment);
    auto t2 = std::thread(print);
    t1.join();
    t2.join();
    return 0;
}

坦率地说,这在典型的现实硬件上令人难以置信地糟糕。最明显的问题是:

查看 increment 中的线程。在 print 运行之前,if 将为 falsewhile 将为真。分支预测将开始变得坚信 if 将是 false.

然后,当 print 线程将 switchFlag 设置为 false 并且您需要 increment 尽快执行,因为另一个线程将要等待为此,你选择了可以想象到的最严重的错误预测分支。

因此,在您尽可能快地执行的最关键时刻,您遇到了处理器可以给您的最差性能,用错误预测的分支炸毁了所有管道。

我强烈建议您不要尝试将原子加载和存储等原始操作组合成复杂的操作。这样做需要深厚的平台专业知识。使用高级函数(如互斥锁和条件变量)进行高级操作(如等待)。

但是一直没有什么好的方法来实现需要交替执行的两个线程。对于两个线程永远无法同时取得进展的任何情况,都没有充分的理由拥有两个线程。