【发布时间】:2020-01-22 11:34:58
【问题描述】:
这是我第一次尝试编写多线程 C++ 代码,它似乎引发了数据竞争。这是完整的文件。编译为:g++ -pthread foo.cpp
#include <iostream>
#include <iomanip>
#include <thread>
const int SIZE = 5;
void mult(int x, int y) {
std::cout.width(3);
std::cout << std::right << x * y << "* ";
}
void add(int x, int y) {
std::cout.width(3);
std::cout << std::right << x + y << "+ ";
}
int main() {
int a = 0;
for (int i = 0; i < SIZE; i++) {
for (int j = 0; j < SIZE; j++) {
std::thread first(mult, i, j);
std::thread second(add, i, j);
first.join();
second.join();
std::cout << " | ";
}
std::cout << "\n";
}
return 0;
}
输出在每次运行时都以不可重现的方式打乱,例如:
0* 0+ | 0* 1+ | 2 0+ * | 0* 3+ | 0* 4+ |
0* 1+ | 1* 2+ | 2* 3+ | 3* 4+ | 4* 5+ |
0* 2+ | 2* 3+ | 4* 4+ | 6* 5+ | 8* 6+ |
0* 3+ | 3 4* + | 6* 5+ | 9* 6+ | 12* 7+ |
0* 4+ | 4* 5+ | 8* 6+ | 12* 7+ | 16* 8+ |
或
0* 0+ | 0* 1+ | 0* 2+ | 0* 3+ | 0* 4+ |
0* 1+ | 1* 2+ | 2* 3+ | 3* 4+ | 4* 5+ |
0* 2+ | 2* 3+ | 4* 4+ | 6* 5+ | 8* 6+ |
0* 3+ | 3* 4+ | 6* 5+ | 9* 6+ | 12* 7+ |
0* 4+ | 4* 5+ | 8* 6+ | 12* 7+ | 16* 8+ |
有没有办法解决这个问题?我从中学到了很多关于 cout 对象的知识,但是规则是否应该一次只允许一个线程访问 cout,尤其是在使用 iomanip 时?
编辑:我理解为: http://www.cplusplus.com/reference/iomanip/setw/ 以这种方式使用 iomanip 可能会导致数据竞争。 所以问题是,不应该尝试这样做吗?是否应该创建每个要 cout 的线程,执行其业务,然后加入? (即根本没有线程),就是这样?如果是这样,那很好,并发的主要思想更多是让程序打开多个并发的 fstream 对象,这样用户就不必等待,一个线程 cout 就可以了。我要问的是,这是标准方法吗?
【问题讨论】:
-
纠正多线程非交错输出的答案非常复杂。我知道 Herb Sutter 在 YouTube 上有一个很棒的视频来处理这个问题。
-
您介意在每个部分中先打印乘法还是除法?如果这样做,则将 IO 放在单独的线程中根本没有意义,让线程计算结果,然后按所需的顺序打印它们。
-
至于交错,我建议有一个单独的函数,其中包含所有
iostream和iomanip功能,由std::mutex通过std::lock_guard保护