【发布时间】:2015-10-20 09:04:02
【问题描述】:
我有一个函数,当被单个线程调用时(直接调用它,或者通过 CreateThread() / WaitForSingleObject() 调用),它的行为正确,但是当被多个 CreateThread() 调用时,它似乎变得混乱,然后是WaitForMultipleObject() 调用。 从我尝试过的大量调试来看,似乎某些作为参数传递给被调用的主函数的变量并未在不同线程之间保持隔离,而是使用相同的地址空间(下面的示例)。以下是问题的一些细节摘要:
首先,我定义一个类型来保存每个线程需要调用的函数的所有参数:
typedef struct {
tDebugInfo DebugParms; int SampleCount; double** Sample; double** Target; double** a; double** F; double** dF; double** prevF; double** prevdF; double*** W; double*** prevW; double*** prevdW; double* e; double* dk; double* dj; double* dj2; double* sk; double* sk2; double* adzev21; double* prevadzev21; double** UW10; double* ro10e; double** dW10d; double** A; double** B; double** C; double** D; double** E; double** G; double** ET; double** AB; double** ABC; double** ABCD; double** ABCDE; double** ABCDH; double** ABCDHG; double** SABCDE; double** SABCDHG; double** I; double** J; double** M; double** x; double** xT; double* xU; double** dW10; int DataSetId; int TestId; int PredictionLen; double* Forecast; double ScaleM; double ScaleP; NN_Parms* ElmanParms; int DP[2][10];} tTrainParams;
然后我分配一个结构数组来保存每个线程的参数集:
HANDLE* HTrain = (HANDLE*)malloc(DatasetsCount*sizeof(HANDLE));
tTrainParams* tp = (tTrainParams*)malloc(DatasetsCount * sizeof(tTrainParams));
DWORD tid = 0; LPDWORD th_id = &tid;
然后,我为每个线程设置函数参数:
tp[d].ElmanParms = pElmanParams; tp[d].SampleCount = SampleCount; tp[d].Sample = SampleData_Scaled[d]; tp[d].Target = TargetData_Scaled[d]; tp[d].a = a; tp[d].F = F; tp[d].dF = dF; tp[d].prevF = prevF; tp[d].prevdF = prevdF; tp[d].W = W; tp[d].prevW = prevW; tp[d].prevdW = prevdW; tp[d].e = e; tp[d].dk = dk; tp[d].dj = dj; tp[d].dj2 = dj2; tp[d].sk = sk; tp[d].sk2 = sk2; tp[d].adzev21 = adzev21; tp[d].prevadzev21 = prevadzev21; tp[d].UW10 = UW10; tp[d].ro10e = ro10e; tp[d].dW10d = dW10d; tp[d].A = A; tp[d].B = B; tp[d].C = C; tp[d].D = D; tp[d].E = E; tp[d].G = G; tp[d].ET = ET; tp[d].AB = AB; tp[d].ABC = ABC; tp[d].ABCD = ABCD; tp[d].ABCDE = ABCDE; tp[d].ABCDH = ABCDH; tp[d].ABCDHG = ABCDHG; tp[d].SABCDE = SABCDE; tp[d].SABCDHG = SABCDHG; tp[d].I = I; tp[d].J = J; tp[d].M = M; tp[d].x = x; tp[d].xT = xT; tp[d].xU = xU; tp[d].dW10 = dW10; tp[d].DebugParms = pDebugParms; tp[d].ElmanParms = pElmanParams; tp[d].PredictionLen = pPredictionLen; tp[d].Forecast = ForecastData[d]; tp[d].ScaleM = ScaleM[d]; tp[d].ScaleP = ScaleP[d]; tp[d].TestId = pTestId; tp[d].DataSetId = d;
然后,我为每个线程调用一个包装函数GetForecastFromTraining(tTrainParams* parms),并在“tp”结构体数组中预先设置了相关参数:
HTrain[d] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)GetForecastFromTraining, &tp[d], 0, th_id);
最后,我调用了 WaitForMultipleObjects():
WaitForMultipleObjects(DatasetsCount, HTrain, TRUE, INFINITE);
GetForecastFromTraining() 对于大多数变量(显然仅限于数组)内部发生的情况是,每当一个线程更改一个数组元素的值(例如,W[0][0][0])时,新值在内部变为当前值所有其他线程也是如此。当然,这会搞砸所有线程中正在进行的所有计算,并且在我看来与跨线程的整个隔离故事相反。
其中一个提示是,当我查看 VS2013 中的“Parallel Watch”调试窗口时,我看到 W 在所有线程中具有相同的地址(因此具有相同的值);但是,&W 对于每个线程都是不同的。其他非数组变量似乎表现良好。最后,我仔细检查了编译器选项中的 /MTd 标志,它就在那里。
我对此很迷茫。有什么建议吗?
P.S.:这是我的程序的简化版本,它显示了相同的问题行为。在此示例中,在 Sleep(1000) 行之后中断执行表明 a1、a2 和 G 变量每个都正确包含线程 id,而 F 对于所有线程都是相同的。
#include <Windows.h>
#include <stdio.h>
#define MAX_THREADS 5
HANDLE h[MAX_THREADS];
typedef struct{
int a1;
int a2;
double* F;
double G[5];
} tMySumParms;
void MySum(tMySumParms* p){
int tid = GetCurrentThreadId();
Sleep(200);
p->a1 = tid;
p->a2 = -tid;
p->F[0] = tid;
p->F[1] = -tid;
p->G[0] = tid;
p->G[1] = -tid;
Sleep(1000);
}
extern "C" __declspec(dllexport) int GetKaz(){
LPDWORD t = NULL;
tMySumParms* p = (tMySumParms*)malloc(MAX_THREADS*sizeof(tMySumParms));
HANDLE* h = (HANDLE*)malloc(MAX_THREADS*sizeof(HANDLE));
double G[5];
double* F = (double*)malloc(5 * sizeof(double));
for (int i = 0; i < MAX_THREADS; i++){
p[i].a1 = 1;
p[i].a2 = 2 ;
p[i].F = F;
memcpy(p[i].G, G, 5 * sizeof(double));
h[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MySum, &p[i], 0, t);
}
WaitForMultipleObjects(MAX_THREADS, h, TRUE, INFINITE);
return 0;
}
【问题讨论】:
-
您必须显示设置 tp 指向的数据的代码。我的猜测是你的初始化有点搞砸了,最后所有不同的 Ws 实际上都引用了相同的内存。
-
我放弃了水平滚动挑战。即使没有这个,你省略了这么多代码的事实意味着我们必须猜测。格式良好的minimal reproducible example 可以让别人帮助你。
-
我刚刚编辑了我的问题,添加了设置 tp 参数的行
标签: c arrays multithreading winapi