【发布时间】:2017-06-20 10:01:12
【问题描述】:
我的作业需要使用多个线程计算 PI。问题是——当使用多个线程时,大多数情况下总和计算不正确(小于应有的值)。我花了很长时间来弄清楚它,所以也许我忽略了一些明显的东西。希望得到您的帮助。
附:有一个关于同一任务的问题。但是,我的错误是不同的。
这是我的代码。
#include <stdio.h>
#include <Windows.h>
#include <ctime>
#define THREAD_COUNT 2
#define N 10000000
#define BLOCK_SIZE 43087
typedef struct {
unsigned startPos;
int threadInd;
double threadSum;
bool isCalculating;
} ThreadArg;
HANDLE* threads;
HANDLE* events;
DWORD WINAPI ThreadProc(LPVOID);
void calculate(double* sum, unsigned start);
int main() {
unsigned cursor = 0;
ThreadArg* params = NULL;
threads = NULL;
if (THREAD_COUNT > 1) {
params = (ThreadArg*)malloc(sizeof(ThreadArg)*(THREAD_COUNT - 1));
threads = (HANDLE*)malloc(sizeof(HANDLE)*(THREAD_COUNT - 1));
events = (HANDLE*)malloc(sizeof(HANDLE)*(THREAD_COUNT - 1));
}
for (int i = 0; i < THREAD_COUNT - 1; ++i) {
ThreadArg arg;
arg.isCalculating = true;
arg.startPos = cursor;
arg.threadInd = i;
arg.threadSum = 0;
params[i] = arg;
threads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, (LPVOID)(¶ms[i]), CREATE_SUSPENDED, 0);
events[i] = CreateEvent(NULL, TRUE, TRUE, NULL);
cursor += BLOCK_SIZE;
}
unsigned startTime = clock();
for (int i = 0; i < THREAD_COUNT - 1; ++i) {
ResumeThread(threads[i]);
}
double mainSum = 0;
while (cursor < N) {
for (int i = 0; i < THREAD_COUNT - 1; ++i) {
if (!params[i].isCalculating) {
params[i].startPos = cursor;
SetEvent(events[i]);
cursor += BLOCK_SIZE;
}
}
calculate(&mainSum, cursor);
cursor += BLOCK_SIZE;
}
bool allFinished;
do {
allFinished = true;
for (int i = 0; i < THREAD_COUNT - 1; ++i) {
if (params[i].isCalculating) {
allFinished = false;
break;
}
}
} while(!allFinished);
for (int i = 0; i < THREAD_COUNT - 1; ++i) {
mainSum += params[i].threadSum;
CloseHandle(threads[i]);
}
printf("Time: %d\n", clock() - startTime);
printf("PI = %.7f\n", mainSum/N);
free(threads);
free(params);
system("pause");
}
DWORD WINAPI ThreadProc(LPVOID lParam) {
ThreadArg* param = (ThreadArg*)lParam;
while (true) {
ResetEvent(events[param->threadInd]);
calculate(¶m->threadSum, param->startPos);
InterlockedDecrement((LONG*)¶m->isCalculating);
WaitForSingleObject(events[param->threadInd], INFINITE);
InterlockedIncrement((LONG*)¶m->isCalculating);
}
}
void calculate(double* sum, unsigned start) {
int endLoop = start + BLOCK_SIZE;
double x;
for (unsigned i = start; i < endLoop; ++i) {
if (i < N) {
x = (i + 0.5) / N;
*sum += 4 / (1 + x*x);
} else break;
}
}
【问题讨论】:
-
你试过在没有线程的情况下运行相同的代码吗? IE。将
ResumeThread调用替换为ThreadProc的直接调用,并检查一切是否正常。 -
它似乎工作正常。当所有计算都发生在主线程中时也可以正常工作。它在接近计算结束时开始出现故障,因为结果在小数点后的第一位或第二位附近波动
-
那么它肯定看起来像竞态条件,这可能是由于线程之间缺乏适当的同步造成的。我可以看到您使用了一些变量/字段,例如
isCalculating与 some 同步甚至原子操作(对于那种问题来说这听起来太难了)。我建议您为每个线程分配一个事件(“完成计算”),然后在主线程中使用WaitForMultipleObjects代替 for 循环。 -
@yeputons 不知道为什么我之前没用过。现在可以用了,非常感谢
-
这……不是一个明智的任务。你处于一个令人羡慕的位置。在这个问题上抛出多个线程实际上并没有什么好处,而且在同步错误和消除这些错误所涉及的不可避免的减速方面会损失很多。计算 pi 不适合这种类型的并行化。开销会扼杀任何可能的性能提升,可能会增加很多倍。
标签: c multithreading winapi