CreateThread 的 threadProc 竞争条件
Posted
技术标签:
【中文标题】CreateThread 的 threadProc 竞争条件【英文标题】:CreateThread's threadProc race condition 【发布时间】:2018-08-16 23:06:33 【问题描述】:我需要生成 4 个线程,它们基本上做同样的事情,但每个线程都有不同的变量。所以我调用 ::CreateThread 4 次,给出相同的 threadProc 和 'this' 作为参数。现在在 threadProc 中,我需要选择正确的变量来使用。我有一个对象向量,并且在每次 CreateThread 调用之后立即将对象推入其中。
// at this point myVec has say, 2 items
HANDLE hThread = ::CreateThread( NULL, NULL, threadProc, (LPVOID)this, NULL, NULL );
myVecObj.threadHandle = hThread;
myVec.push_back(myVecObj); // myVec.Size = 3 now
DWORD CALLBACK myClass::threadProc(LPVOID lpContext)
myClass *pMyClass = (myClass *)lpContext;
int vecCount = pMyClass->myVec.size; // Is this 3??
char * whatINeed = (char*)pMyClass->myVec[vecCount-1].whatINeed;
我的疑问/问题是 threadProc 的触发速度有多快 - 它能否击败对 myVec.push_back() 的调用?这是我在这里介绍的比赛条件吗?我试图假设当每个 threadProc 开始时(它们在不同的时间开始,而不是一个接一个地开始),我可以安全地获取类向量中的最后一个对象。
【问题讨论】:
使用_beginthreadex()
和全局范围 f
【参考方案1】:
我需要生成 4 个线程,它们基本上做同样的事情,但每个线程都有不同的变量。所以我调用
::CreateThread
4 次,将threadProc
和this
作为参数。现在在
threadProc
,我需要选择正确的变量来使用。
为什么不向线程传递一个指向它需要操作的实际对象的指针?
我有一个对象向量,我在每次调用
CreateThread
后立即将对象推入其中。
这是错误的处理方式。是的,这是一个竞争条件。不仅因为显而易见的原因 - 线程可能在对象被推送之前开始运行 - 而且因为 any 推送到向量中可能会重新分配向量的内部数组,这对线程来说是非常糟糕的已经获得了指向其向量内数据的指针。数据会在线程背后的内存中移动。
要解决这个问题,您需要:
首先将所有对象推入向量中,然后启动线程。您可以将指向每个向量元素的指针传递给其各自的线程。但这仅在您在任何线程运行时不再修改向量时才有效,原因如上所述。
首先以挂起状态启动线程,然后在将所有对象推入向量后恢复它们。这也要求您不再修改向量。这也意味着您必须向每个线程传递一个向量元素的索引,而不是传递一个指向该元素的指针。
完全摆脱向量(或至少将其更改为保存对象指针而不是实际对象)。使用new
动态分配您的对象,并根据需要将这些指针传递给每个线程(也可以选择传递给向量)。在退出之前让每个线程delete
其对象(并可选择将其从向量中移除,并进行适当的同步)。
我的疑问/问题是
threadProc
起火的速度有多快
这完全取决于操作系统调度程序来决定。
它能否击败对
myVec.push_back()
的调用?
是的,这是可能的。
这是我在这里介绍的竞争条件吗?
是的。
我试图做出假设
不要做假设!
当每个
threadProc
开始时(它们在不同的时间开始,而不是一个接一个),我可以安全地获取类向量中的最后一个对象。
这不是一个安全的假设。
【讨论】:
很好的答案。感谢您的反馈。【参考方案2】:修改myVec
,即myVec.push_back()
调用,和在另一个线程中读取对象的大小没有同步。我确实意识到您不使用标准线程但应用 C++11 规则存在数据竞争并且程序具有未定义的行为。
请注意,数据竞争不仅仅是理论上的:您很有可能看到修改发生在读取之后。创建线程可能不会很快,但有些实现实际上并没有创建操作系统级别的线程,而是保留了一个线程池,显然在产生新线程时会使用这些线程池。
在类似的情况下,我听到了一个很好的论点“......但它只发生一百万次!”。假设“百万分之一”的估计是正确的,那么这个特殊问题在 48 核机器上大约每秒会发生 10 次。
【讨论】:
谢谢。我绝对打算在 myVec 上添加同步——但这无助于我遇到的问题。我想我需要找到一种更好的方法来区分不同的 threadProc 调用。以上是关于CreateThread 的 threadProc 竞争条件的主要内容,如果未能解决你的问题,请参考以下文章
C++怎么在主线程中使用子线程的数据? 比如说主线程中有一个数组,如何在子线程中调用这个数组