3

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

4

2 回答 2

2

您可以使用std::mutexstd::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;
}

在这个例子中,多线程没有意义,但在更大的例子中,你只会保护输入/输出。计算并行运行。

于 2019-09-21T23:37:20.237 回答
1

在这种情况下,最好只从主线程中完成所有输出:

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

void mult(int &res, int x, int y) {
    res = x * y;
}

void add(int &res, int x, int y) {
    res = x + y;
}

int main() {
    int a = 0;
    for (int i = 0; i < SIZE; i++) {
        for (int j = 0; j < SIZE; j++) {
            int mult_res, add_res;
            std::thread first(mult, std::ref(mult_res), i, j);
            std::thread second(add, std::ref(add_res), i, j);
            first.join();
            second.join();
            std::cout.width(3);
            std::cout << std::right << mult_res << "* ";
            std::cout.width(3);
            std::cout << std::right << add_res << "+ | " ;
        }
        std::cout << "\n";
    }
    return 0;
}
于 2019-09-21T23:57:26.307 回答