繁体   English   中英

多线程和参数混合

[英]multithreading and parameters mixup

我有一个函数,该函数在被单个线程调用时可以正确运行(直接调用它,或者通过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标志,它在那里。

我对此很迷茫。 有什么建议吗?

PS:这是我程序的简化版本,它显示出相同的问题行为。 在此示例中,在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;
}

W在参数struct中声明为double*** ,稍后在您说的问题中,您将其用作W[0][0][0] 因此, W是一个指针数组,该指针数组指向一个双精度数组。
我的猜测是所有线程都属于这些层之一。

为了证实这一理论,并确保它不是并发问题,而是数据结构问题,我将创建一个简单的单线程测试函数,如下所示:

  • 1.0填充用于线程1的数组
  • 然后用2.0填充线程2的数组
  • 检查线程1的值。

简化的版本显示了问题: F数组分配一次,并且每个线程都获得指向该单个数组的指针。 因此,如果一个线程更新了数组,其他所有线程都将看到更改。

double* F = (double*)malloc(5 * sizeof(double));    // one array!
for (int i = 0; i < MAX_THREADS; i++){
    ...
    p[i].F = F;                     // all threads use the same array!

更改为:

for (int i = 0; i < MAX_THREADS; i++){
    ...
    p[i].F = malloc(5 * sizeof(double)); // each thread has its own array

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM