if 语句仅在前面有调试 cout 行(C 中的多线程)时才通过

If statement passes only when preceded by debug cout line (multi-threading in C)

本文关键字:多线程 在前面 语句 调试 cout if      更新时间:2023-10-16

我创建了这段代码,用于实时解决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,并且由于主线程仅在所有工作线程都在等待之前读取它,因此如果(threadone_private == 线程计数(应该是防弹的......显然,每当我更改它时,我都会错过一些重要的东西:

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++缺乏良好的内置高级并发构造,但有一些库可以处理这个问题。

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

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

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

我在代码中看不到任何std::mutex,std::condition_variable或std::lock的变体。在没有这些的情况下进行多线程永远不会可靠地成功。因为每当多个线程修改相同的数据时,您需要确保在任何给定时间只有一个线程(包括主线程(可以访问该数据。

编辑:我注意到你使用原子。我对此没有任何经验,但是我知道使用互斥体可以可靠地工作。

因此,您需要使用如下所示的互斥锁锁定对该数据的每次访问(读取或写入(:

//somewhere
std::mutex myMutex;
std::condition_variable myCondition;
int workersDone = 0;
/* main thread */
createWorkerThread1();
createWorkerThread2();
{
std::unique_lock<std::mutex> lock(myMutex); //waits until mutex is locked.
while(workersDone != 2) {
myCondition.wait(lock); //the mutex is unlocked while waiting
}
std::cout << "the data is ready now" << std::endl;
} //the lock is destroyed, unlocking the mutex
/* Worker thread */
while(true) {
{
std::unique_lock<std::mutex> lock(myMutex); //waits until mutex is locked
if(read_or_modify_a_piece_of_shared_data() == DATA_FINISHED) {
break; //lock leaves the scope, unlocks the mutex
}
}
prepare_everything_for_the_next_piece_of_shared_data(); //DO NOT access data here
}
//data is processed
++workersDone;
myCondition.notify_one(); //no mutex here. This wakes up the waiting thread

我希望这能让您了解如何使用互斥体和条件变量来获得线程安全性。

免责声明:100%伪代码;)