如何在循环中使用_beginthreadex并保持并行性

时间:2016-07-26 06:48:31

标签: c++ multithreading

  HANDLE h[4]; 
  for(int i=0;i<test_img_num;i++)
     h[i] = (HANDLE)_beginthreadex(0, 0, (unsigned int (__stdcall *)(void *))ThreadFunc, &test_images[i], 0, 0);  
  WaitForMultipleObjects(4,h,true,INFINITE);

test_images是vector<Mat_<uchar> > test_images;

我的ThreadFunc是,

Mat_<double> _stdcall ThreadFunc( void * param)
{
    Mat_<uchar> *img=(Mat_<uchar> *)param;
     BoundingBox temp;
    temp.start_x=temp.start_y=0;
    temp.width=img->cols;
    temp.height=img->rows;
    temp.centroid_x = temp.start_x + temp.width/2.0;
    temp.centroid_y = temp.start_y + temp.height/2.0; 
    Mat_<double> current_shape = regressor.Predict(*img,temp,INITIAL_NUMBER);
    return current_shape;
} 

weirld的事情是它有时运行正常,但有时它会崩溃(在线程img中无法读取,“内存访问冲突”)。通过谷歌,我发现当子线程运行时test_images[i]发生更改,而i=5test_images[i]无法读取。我想这可能是问题所在。我了解到我可以使用锁来防止test_images[i]意外更改。

然而,问题是我想要的是在四个线程之间保持ThreadFunc中Predict函数的并行性,但是如果我锁定i,第二个线程必须等到第一个线程的Predict操作完成并且那里将没有平行性。

我该如何解决这个问题?非常感谢!

更新:我直接使用

    h[0] = (HANDLE)_beginthreadex(0, 0, (unsigned int (__stdcall *)(void *))ThreadFunc, &test_images[0], 0, 0); 
  h[1] = (HANDLE)_beginthreadex(0, 0, (unsigned int (__stdcall *)(void *))ThreadFunc, &test_images[1], 0, 0); 
  h[2] = (HANDLE)_beginthreadex(0, 0, (unsigned int (__stdcall *)(void *))ThreadFunc, &test_images[2], 0, 0); 
  h[3] = (HANDLE)_beginthreadex(0, 0, (unsigned int (__stdcall *)(void *))ThreadFunc, &test_images[3], 0, 0);

执行temp.width=img->cols;时仍会发出“内存访问冲突”的警告或许i的更改不是问题吗?

1 个答案:

答案 0 :(得分:0)

永远不要转换函数指针。

更准确地说,你可以暂时转换它们,但是你必须永远不要调用(或允许被调用)一个不指向具有匹配类型的函数的函数指针。

违反此规则是未定义的行为。以下是对内部出错的描述,但这只是技术背景信息。

在您的情况下,ThreadFunc会返回Mat_<double>,但_beginthreadex需要一个返回unsigned int的函数,因此您需要转换函数指针而不是将函数更改为符合预期。

为什么这有关系?因为返回类型不会被简单地忽略。该函数必须将返回值存储在某处,调用函数可能希望使用它。 _beginthreadex将线程函数的结果作为退出代码传递给ExitThread,然后您可以使用GetExitCodeThread检索该代码。通过返回不是unsigned int的东西,你可能会在那里得到垃圾。

但这是一个小问题。最大的问题是当你返回一个类类型时会发生什么。在Windows调用约定(以及大多数其他约定)中,复杂的结构返回值被写入调用者分配的堆栈空间,通过作为隐藏的附加参数传递给被调用者的指针。但是如果调用者认为函数返回unsigned int,它将不会分配堆栈空间或传递隐藏的指针。当被调用者想要写入返回值时,它将读取它所期望的隐藏参数,但它只是一些随机值,然后尝试写入它读取的地址。这将导致它覆盖随机内存,或者,如果幸运的话,会产生访问冲突,立即导致程序崩溃。

顺便说一下,您应该考虑使用std::vector<std::thread>而不是使用_beginthreadex创建线程。线程类在允许调用的内容方面更灵活,可以更容易地将上下文传递给线程函数,并自动处理线程句柄的清理。