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=5
,test_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的更改不是问题吗?
答案 0 :(得分:0)
永远不要转换函数指针。
更准确地说,你可以暂时转换它们,但是你必须永远不要调用(或允许被调用)一个不指向具有匹配类型的函数的函数指针。
违反此规则是未定义的行为。以下是对内部出错的描述,但这只是技术背景信息。
在您的情况下,ThreadFunc
会返回Mat_<double>
,但_beginthreadex
需要一个返回unsigned int
的函数,因此您需要转换函数指针而不是将函数更改为符合预期。
为什么这有关系?因为返回类型不会被简单地忽略。该函数必须将返回值存储在某处,调用函数可能希望使用它。 _beginthreadex
将线程函数的结果作为退出代码传递给ExitThread
,然后您可以使用GetExitCodeThread
检索该代码。通过返回不是unsigned int
的东西,你可能会在那里得到垃圾。
但这是一个小问题。最大的问题是当你返回一个类类型时会发生什么。在Windows调用约定(以及大多数其他约定)中,复杂的结构返回值被写入调用者分配的堆栈空间,通过作为隐藏的附加参数传递给被调用者的指针。但是如果调用者认为函数返回unsigned int
,它将不会分配堆栈空间或传递隐藏的指针。当被调用者想要写入返回值时,它将读取它所期望的隐藏参数,但它只是一些随机值,然后尝试写入它读取的地址。这将导致它覆盖随机内存,或者,如果幸运的话,会产生访问冲突,立即导致程序崩溃。
顺便说一下,您应该考虑使用std::vector<std::thread>
而不是使用_beginthreadex
创建线程。线程类在允许调用的内容方面更灵活,可以更容易地将上下文传递给线程函数,并自动处理线程句柄的清理。