删除不同位置的指针会导致不同的行为(崩溃与否)

时间:2009-08-19 10:42:57

标签: c++ multithreading pointers memory-management heap

这个问题是对this one的一种改进,它与预期的方向不同。

在我的多线程应用程序中,主线程创建参数并存储它们:

typedef struct {
    int parameter1;
    double parameter2;
    float* parameter3;
} jobParams;

typedef struct {
    int ID;
    void* params;
} jobData;

std::vector<jobData> jobs;

// main thread
for (int i = 0; i < nbJobs; ++i) {
    jobParams* p = new jobParams;
    // fill and store params

    jobData data;
    data.ID = i;
    data.params = p;

    jobs.push_back(data);
}

// start threads and wait for their execution

// delete parameters
for (int i = 0; i < jobs.size(); ++i) {
    delete jobs[i].params;
}

然后,每个线程获得一个指向一组参数的指针,并用它调用一个作业函数:

// thread (generic for any job function and any type of params)
jobData* job = main->getNextParams();
jobFunction(job->ID, job->params);

整个事情采用void *作为参数,以便能够使用参数的任何结构,但然后作业函数将其转换回正确的结构:

void* jobFunction(void* param) {
    jobParams* params = (jobParams*) param;
    // do stuff
    return 0;
}

我的问题如下:如果我在delete params结束时jobFunction(),则效果非常好。但是,我宁愿让线程或主线程删除删除,这样我就不必记住删除我写的每个jobFunction()的参数。

如果我在确认所有线程都已完成后(或因此不再需要参数)之后在踏板中调用delete params之后尝试jobFunction(),或者甚至在主线程中尝试{{1}}得到堆损坏错误:

  

HEAP [prog]:为RtlFreeHeap(02E90000,03C2EE38)指定的无效地址

我正在使用Visual Studio 2008 Pro,因此我无法使用valgrind或其他* nix工具进行调试。从“子线程”对主线程的所有访问都是使用互斥锁同步的,所以问题不在于我删除了两次相同的参数。

事实上,通过使用VS内存查看器,我知道jobParams指针所指向的内存在jobFunction()的结尾和我尝试删除它的位置之间没有变化(在主线程中或在主线程中) “子线程”)。

我添加了两种结构的定义,以及我想删除params的方式。

3 个答案:

答案 0 :(得分:2)

就像一个想法......你能试试吗

for (int i = 0; i < jobs.size(); ++i) {
    delete (jobParams*)jobs[i].params;
}

新建一个类型的jobParams然后删除一个void *可能是你的问题的原因。

有没有理由将params存储为jobData中的void *?我认为,如果你希望有不同类型的jobParams,那么你应该使用继承层次结构,而不是盲目地转向虚空*。

答案 1 :(得分:1)

这种错误通常意味着你在某处有数据竞争。 main-&gt; getNextParams()是否做正确的事情,即使它被多个线程一次调用?如果它给两者都提供相同的参数,你可以手上有双倍的免费。

此外,而不是

jobFunction(jobData->ID, jobData->params);

你可能意味着

jobFunction(job->ID, job->params);

答案 2 :(得分:1)

要调试它,您可以将deleted成员添加到jobParams类,并将其设置为true,而不是实际删除该对象。然后查看deleted的每个方法中的jobParams标志,如果是,则抛出异常。然后看看抛出异常的位置。