首页 > 解决方案 > 如果语句仅在调试 cout 行之前通过(C 中的多线程)

问题描述

我创建了这段代码,用于实时解决 CPU 密集型任务,并可能作为未来游戏引擎的基础。为此,我创建了一个系统,其中每个线程都会修改一个整数数组,以指示它们是否已完成当前任务。

使用超过 4 个线程运行它时会出现问题。当使用 6 个或更多线程时,“if (threadone_private == threadcount)”停止工作,除非我添加了这个调试行“cout << threadone_private << endl;” 在它之前。

我无法理解为什么此调试行对 if 条件函数是否按预期产生任何影响,也无法理解为什么在使用 4 个或更少线程时没有它也能工作。

对于此代码,我正在使用:

#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <iostream>
#include <thread>
#include <atomic>
#include <vector>
#include <string>
#include <fstream>
#include <sstream>

using namespace std;

目前这段代码最多只能计算到 60 万亿,异步步长为 30 亿,非常快。

以下是代码的相关部分:

int thread_done[6] = { 0,0,0,0,0,0 };
atomic<long long int> testvar1 = 0;
atomic<long long int> testvar2 = 0;
atomic<long long int> testvar3 = 0;
atomic<long long int> testvar4 = 0;
atomic<long long int> testvar5 = 0;
atomic<long long int> testvar6 = 0;

void task1(long long int testvar, int thread_number)
{
    int continue_work = 1;
    for (; ; ) {
        while (continue_work == 1) {
            for (int i = 1; i < 3000000001; i++) {
                testvar++;
            }
            thread_done[thread_number] = 1;
            if (thread_number==0) {
                testvar1 = testvar;
            }
            if (thread_number == 1) {
                testvar2 = testvar;
            }
            if (thread_number == 2) {
                testvar3 = testvar;
            }
            if (thread_number == 3) {
                testvar4 = testvar;
            }
            if (thread_number == 4) {
                testvar5 = testvar;
            }
            if (thread_number == 5) {
                testvar6 = testvar;
            }
            continue_work = 0;
        }
        if (thread_done[thread_number] == 0) {
            continue_work = 1;
        }
    }
}

这是主线程的相关部分:

int main() {
    long long int testvar = 0;
    int threadcount = 6;
    int threadone_private = 0;
    thread thread_1(task1, testvar, 0);
    thread thread_2(task1, testvar, 1);
    thread thread_3(task1, testvar, 2);
    thread thread_4(task1, testvar, 3);
    thread thread_5(task1, testvar, 4);
    thread thread_6(task1, testvar, 5);
    for (; ; ) {
        if (threadcount == 0) {
            for (int i = 1; i < 3000001; i++) {
                testvar++;
            }
            cout << testvar << endl;
        }
        else {
            while (testvar < 60000000000000) {
                threadone_private = thread_done[0] + thread_done[1] + thread_done[2] + thread_done[3] + thread_done[4] + thread_done[5];
                cout << threadone_private << endl;
                if (threadone_private == threadcount) {
                    testvar = testvar1 + testvar2 + testvar3 + testvar4 + testvar5 + testvar6;
                    cout << testvar << endl;
                    thread_done[0] = 0;
                    thread_done[1] = 0;
                    thread_done[2] = 0;
                    thread_done[3] = 0;
                    thread_done[4] = 0;
                    thread_done[5] = 0;
                }
            }
        }
    }
}

我希望因为每个工作线程只修改数组threadone_private中的一个int,并且由于主线程只在所有工作线程都等待之前读取它,所以这个if(threadone_private == threadcount)应该是防弹的......显然我'我错过了一些重要的东西,每当我改变它时就会出错:

                threadone_private = thread_done[0] + thread_done[1] + thread_done[2] + thread_done[3] + thread_done[4] + thread_done[5];
                cout << threadone_private << endl;
                if (threadone_private == threadcount) {

对此:

threadone_private = thread_done[0] + thread_done[1] + thread_done[2] + thread_done[3] + thread_done[4] + thread_done[5];
//cout << threadone_private << endl;
                if (threadone_private == threadcount) {

标签: c++multithreading

解决方案


免责声明:并发代码非常复杂且容易出错,因此使用更高级别的抽象通常是个好主意。有很多细节很容易在没有注意到的情况下出错。如果您不是专家,您应该非常仔细地考虑进行此类低级编程。遗憾的是,C++ 缺乏良好的内置高级并发构造,但有一些库可以处理这个问题。

目前还不清楚整个代码应该对我做什么。据我所知,代码是否停止完全依赖于时间——即使你正确地进行了同步——这完全是不确定的。您的线程可以以一种thread_done永远不会完全正确的方式执行。

但除此之外,至少还有一个正确性问题:您在int thread_done[6] = { 0,0,0,0,0,0 };没有同步的情况下进行读写。这是未定义的行为,因此编译器可以做它想做的事。

可能发生的情况是编译器看到它可以缓存 的值,threadone_private因为线程从不写入它,因此该值不能改变(合法地)。外部调用std::cout意味着它不能确定值不会在其背后发生变化,因此它必须在每次迭代新的时候读取值(std::cout 也使用锁,这在大多数实现中会导致同步,这再次限制了编译器可以假设)。


推荐阅读