c++ - 在多线程程序中使用 std::cout 和 <iomanip> 时如何避免数据竞争?

标签 c++ c++11

这是我第一次尝试编写多线程 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 就可以了。我想问的是,这是标准方法吗?

最佳答案

你可以使用 std::mutex和一个 std::lock_guard :

#include <iomanip>
#include <iostream>
#include <mutex>
#include <thread>
const int SIZE = 5;

std::mutex iomutex;

void mult(int x, int y) {
    // Complex, time-consuming calculations run multithreaded
    auto res = x * y;
    // lock stops other threads at this point
    std::lock_guard<std::mutex> lock(iomutex);
    // IO is singlethreaded
    std::cout.width(3); 
    std::cout << std::right << res << "* ";
    // lock leaves scope and is unlocked, next thread can start IO
}

void add(int x, int y) {
    // Complex, time-consuming calculations run multithreaded
    auto res = x + y;
    // lock stops other threads at this point
    std::lock_guard<std::mutex> lock(iomutex);
    // IO is singlethreaded
    std::cout.width(3); 
    std::cout << std::right << res << "+ ";
    // lock leaves scope and is unlocked, next thread can start IO
}

int main() {
    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;
}

在此示例中,多线程没有任何意义,但在更大的示例中,您只会保护输入/输出。计算并行运行。

关于c++ - 在多线程程序中使用 std::cout 和 <iomanip> 时如何避免数据竞争?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58044802/

相关文章:

C++ vector.push_back 在指针定义后崩溃

c++ - 如何为自定义字符串类型编写构造函数/赋值运算符重载?

c++ - 如何在运行时以最小的开销共享全局常量?

java - 二分查找条件

c++ - 着色玩具到 SFML

c++ - constexpr 模板参数怪异

c++ - 抑制 GTK+ 中的 Valgrind 错误

c++ - boost::filesystem 递归获取每个文件的大小

c++ - 如何使 '=' 重载在 '=' 的另一端工作

c++ - 处理具有不同类型参数的 std::basic_string<>