我是一个初学者,我试图重现一个rae条件,以便让自己熟悉这个问题。为此,我创建了以下程序:
#include <Windows.h>
#include <iostream>
using namespace std;
#define numThreads 1000
DWORD __stdcall addOne(LPVOID pValue)
{
int* ipValue = (int*)pValue;
*ipValue += 1;
Sleep(5000ull);
*ipValue += 1;
return 0;
}
int main()
{
int value = 0;
HANDLE threads[numThreads];
for (int i = 0; i < numThreads; ++i)
{
threads[i] = CreateThread(NULL, 0, addOne, &value, 0, NULL);
}
WaitForMultipleObjects(numThreads, threads, true, INFINITE);
cout << "resulting value: " << value << endl;
return 0;
}
我在线程函数中添加了睡眠,以便重现竞争条件,我理解,如果我只是添加一个作为工作负载,竞争条件不会表现出来:一个线程是创建,然后它运行工作负载,它恰好在另一个迭代创建的另一个线程开始其工作负载之前完成。我的问题是工作负载内的Sleep()似乎被忽略了。我将参数设置为5秒,我希望程序运行至少5秒,但它会立即完成。当我将Sleep(5000)置于main函数内时,程序按预期运行(> 5秒)。为什么Sleep内部线程被忽略? 但无论如何,即使Sleep()被忽略,程序也会在每次启动时输出:
结果值:1000
虽然正确的答案应该是2000.你能猜出为什么会这样吗?
答案 0 :(得分:5)
WaitForMultipleObjects一次只允许等待最多MAXIMUM_WAIT_OBJECTS(当前是64个)线程。如果你考虑到这一点:
#include <Windows.h>
#include <iostream>
using namespace std;
#define numThreads MAXIMUM_WAIT_OBJECTS
DWORD __stdcall addOne(LPVOID pValue) {
int* ipValue=(int*)pValue;
*ipValue+=1;
Sleep(5000);
*ipValue+=1;
return 0;
}
int main() {
int value=0;
HANDLE threads[numThreads];
for (int i=0; i < numThreads; ++i) {
threads[i]=CreateThread(NULL, 0, addOne, &value, 0, NULL);
}
WaitForMultipleObjects(numThreads, threads, true, INFINITE);
cout<<"resulting value: "<<value<<endl;
return 0;
}
......事情的发挥更像你期望的那样。当然,你是否真的会看到竞争条件的结果是一个相当不同的故事 - 但是在多次运行中,我确实看到了结果值的微小变化(例如,低于125左右)。
答案 1 :(得分:1)
Jerry Coffin有正确的答案,但只是为了省你打字:
#include <Windows.h>
#include <iostream>
#include <assert.h>
using namespace std;
#define numThreads 1000
DWORD __stdcall addOne(LPVOID pValue)
{
int* ipValue = (int*)pValue;
*ipValue += 1;
Sleep(5000);
*ipValue += 1;
return 0;
}
int main()
{
int value = 0;
HANDLE threads[numThreads];
for (int i = 0; i < numThreads; ++i)
{
threads[i] = CreateThread(NULL, 0, addOne, &value, 0, NULL);
}
DWORD Status = WaitForMultipleObjects(numThreads, threads, true, INFINITE);
assert(Status != WAIT_FAILED);
cout << "resulting value: " << value << endl;
return 0;
}
如果出现问题,请确保已断言任何可能失败的Windows API函数的返回值。如果你真的非常需要等待很多线程,可以通过链接来克服64线程限制。即,对于您需要等待的每64个额外线程,您牺牲了一个线程,其唯一目的是等待64个其他线程,依此类推。我们(Windows Developer's Journal)多年前发表了一篇展示该技术的文章,但我无法回想起作者的名字。