从另一个线程终止C++中的线程
基本上我找到两个线程的素数。我将每个线程的可能素数范围分为一半,或者静态分配线程之间的范围。然而,必须处理较小数字的线程将在计算较大数字之前完成。我想要做的就是一旦任何一个线程通过它的范围,终止这两个线程,然后将剩下的未完成的线程剩下的一半返回给那些已经完成的线程,以便它们递归地平均将始终并行运行。 例如:A(1-100)和B(100-200),A在B仍在150时首先结束。两者都停止,A开始像A(150-175),B开始像B(175-200)。从另一个线程终止C++中的线程
这是到目前为止我的代码:
#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <queue>
#include <cmath>
#include <iostream>
using namespace std;
priority_queue<int> primes;
CRITICAL_SECTION critical;
struct args
{
int begin;
int end;
}par1, par2;
int e_prosto(int n)
{
for(int i = 2; i*i<(n + 1) ; i++)
if (n & 1 == 0 || n % i == 0) return 0;
return 1;
}
unsigned int __stdcall rabotnik(void* n)
{
struct args *lPar = (args*) n;
for(int i = lPar->begin; i < lPar->end; i++)
{
if(e_prosto(i)){
EnterCriticalSection(&critical);
primes.push(i);
LeaveCriticalSection(&critical);
}
}
return 0;
}
int main()
{
int foo;
printf(" Tarsene na prosti do: ");
scanf("%d", &foo);
par1.begin=1;
par1.end=foo/2+1;
par2.begin=foo/2+1;
par2.end=foo;
HANDLE hvadkaA, hvadkaB;
InitializeCriticalSection(&critical);
SYSTEMTIME st, now, then;
hvadkaA = (HANDLE)_beginthreadex(0, 0, &rabotnik, (void*)&par1, 0, 0);
hvadkaB = (HANDLE)_beginthreadex(0, 0, &rabotnik, (void*)&par2, 0, 0);
::GetSystemTime(&then);
WaitForSingleObject(hvadkaA, INFINITE);
WaitForSingleObject(hvadkaB, INFINITE);
CloseHandle(hvadkaA);
CloseHandle(hvadkaB);
::GetSystemTime(&now);
while(!primes.empty())
{
printf("%d \t", primes.top());
primes.pop();
}
printf("\nGotov za %d milisec", abs(now.wMilliseconds - then.wMilliseconds));
system("pause>nul");
return 0;
}
我建议不要给每个线程一个块A(1-100)和B(101-200),每个线程被分配一个模。例如,A将取所有奇数指数,B取所有偶数指数,得到A {1,3,5,7,9,...,191,193,195,197,199}和B {2,4,6,8,..., 190,192,194,196,198,200}。这可能是负载平衡线程的最快和最简单的方法,因为计算的复杂性将均匀分布。
下一个建议是添加一个bool指示是否可以继续处理。然后,在每次计算开始之前,检查是否可以继续。通过这种方式,您可以在不终止线程的情况下停止计算,但每次循环需要额外比较一次。
#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <queue>
#include <cmath>
#include <iostream>
using namespace std;
bool run;
priority_queue<int> primes;
CRITICAL_SECTION critical;
struct args
{
int begin;
int end;
}par1, par2;
int e_prosto(int n)
{
for(int i = 2; i*i<(n + 1) ; i++)
if (n & 1 == 0 || n % i == 0) return 0;
return 1;
}
unsigned int __stdcall rabotnik(void* n)
{
struct args *lPar = (args*) n;
for(int i = lPar->begin; i < lPar->end && run; i++)
{
if(e_prosto(i)){
EnterCriticalSection(&critical);
primes.push(i);
LeaveCriticalSection(&critical);
}
}
run = false;
return 0;
}
int main()
{
int foo;
printf(" Tarsene na prosti do: ");
scanf("%d", &foo);
par1.begin=1;
par1.end=foo/2+1;
par2.begin=foo/2+1;
par2.end=foo;
run = true;
HANDLE hvadkaA, hvadkaB;
InitializeCriticalSection(&critical);
SYSTEMTIME st, now, then;
hvadkaA = (HANDLE)_beginthreadex(0, 0, &rabotnik, (void*)&par1, 0, 0);
hvadkaB = (HANDLE)_beginthreadex(0, 0, &rabotnik, (void*)&par2, 0, 0);
::GetSystemTime(&then);
WaitForSingleObject(hvadkaA, INFINITE);
WaitForSingleObject(hvadkaB, INFINITE);
CloseHandle(hvadkaA);
CloseHandle(hvadkaB);
::GetSystemTime(&now);
while(!primes.empty())
{
printf("%d \t", primes.top());
primes.pop();
}
printf("\nGotov za %d milisec", abs(now.wMilliseconds - then.wMilliseconds));
system("pause>nul");
return 0;
}
另一种方法是将您的范围分割成许多块,然后当一个线程完成时给它一个新的块来处理。这样做的好处是不会增加额外的计算开销,但确实需要更多的代码(因此,您正在重复使用线程并监听任何线程的完成,而不仅仅是一个)。要有任何价值,那么你可能需要更大的范围,你可能需要根据复杂性(块大小{1-100},{101-150},{151-175},{176-183}), {184-187},...)。使用代码的快速示例(使用对称块大小):
#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <queue>
#include <cmath>
#include <iostream>
using namespace std;
priority_queue<int> primes;
CRITICAL_SECTION critical;
typedef struct args
{
int begin;
int end;
//Helper method for initalizing struct
void setAll(int inBegin, bool inEnd)
{
}
} *PArgs;
int e_prosto(int n)
{
for(int i = 2; i*i<(n + 1) ; i++)
if (n & 1 == 0 || n % i == 0) return 0;
return 1;
}
static DWORD WINAPI rabotnik(LPVOID lpParam)
{
struct args *lPar = (args*) lpParam;
for(int i = lPar->begin; i < lPar->end; i++)
{
if(e_prosto(i)){
EnterCriticalSection(&critical);
primes.push(i);
LeaveCriticalSection(&critical);
}
}
return 0;
}
int main()
{
const int NUM_THREAD = 2; //Use named constant incase you want to change later.
DWORD returnedThreadID;
DWORD threadID[NUM_THREAD];
HANDLE threadHandle[NUM_THREAD]; //Holds the handels for the threads.
int foo, //Range size.
fooBlockSize, //Number of elements in a block.
nextBlock;
PArgs par[NUM_THREAD];
printf(" Tarsene na prosti do: ");
scanf("%d", &foo); //Get range size from user.
fooBlockSize = foo/(NUM_THREAD * 10); //Set number of elements in a block.
InitializeCriticalSection(&critical);
SYSTEMTIME st, now, then;
for (int i = 0; i < NUM_THREAD; i++)
{
par[i] = (PArgs) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PArgs));
// If the allocation fails, the system is out of memory so terminate execution.
if(par[i] == NULL){ cout<<"par HeapAlloc failed with error# "<<GetLastError()<<endl<<"Will now quit."<<endl; ExitProcess(2);}
}
for(int i = 0; i < NUM_THREAD; i++)
{
par[i]->begin = (fooBlockSize * i) + 1;
par[i]->end = par[i]->begin + fooBlockSize;
threadHandle[i] = CreateThread(NULL, 0, rabotnik, par[i], CREATE_SUSPENDED, &threadID[i]);
}
nextBlock = NUM_THREAD;
::GetSystemTime(&then);
for (int i = 0; i < NUM_THREAD; i++)
{
ResumeThread(threadHandle[i]); //Start threads
}
while(((fooBlockSize * nextBlock) + 1) < foo)
{
returnedThreadID = WaitForMultipleObjects(NUM_THREAD, threadHandle, false, INFINITE); //Wait for a thread to complete.
for(int i = 0; i<NUM_THREAD;i++)
{
if(returnedThreadID = threadID[i])
{
//Update the thread's arguments with the new block.
par[i]->begin = (fooBlockSize * nextBlock) + 1;
par[i]->end = par[i]->begin + fooBlockSize;
//Restart the thread.
ResumeThread(threadHandle[i]);
nextBlock++;
}
}
}
for (int i = 0; i < NUM_THREAD; i++)
{
//Return heap memorry (good practice, though Windows should return it all when the process terminates).
if (HeapFree(GetProcessHeap(), 0, par[i]) == 0)
{
cout<<"HeapFree failed for par["<<i<<"]"<<endl;
}
//Not sure we need to close the threads, but it was in original version.
CloseHandle(threadHandle[i]);
}
::GetSystemTime(&now);
while(!primes.empty())
{
printf("%d \t", primes.top());
primes.pop();
}
printf("\nGotov za %d milisec", abs(now.wMilliseconds - then.wMilliseconds));
system("pause>nul");
return 0;
}
在增加块数与块大小之间有一个折衷。增加块的数量意味着只有一个块能够处理的时间会更少(线程[0]完成,并且在线程[1]结束时没有剩下任何处理),但也意味着将会有我花了更多时间等待调度程序循环分配一个新块来处理。用你的问题陈述,我期望找到更高级别的素数需要很长时间,这并不重要。
正如其他答案所指出的,不要使用相同的堆栈来存储每个线程找到的主要值(工作锁所需的时间过长)。如果你想以数字顺序返回素数,我建议重写打印素数的循环,以便它同时通过两个堆栈,打印下一个值(按顺序)。有些东西,如:
while(!primes1.empty() && !primes2.empty())
{
if(primes1.top() > primes2.top())
{
printf("%d \t", primes1.top());
primes1.pop();
}
else
{
printf("%d \t", primes2.top());
primes2.pop();
}
}
你将不得不应对一个堆遗留下来的价值观一旦另一个是空的(或者放置一个-1在每个堆栈的底部,所以,如果你要么堆栈为空,则所有大于-1的值都将被打印)。
另一种解决方案是维护每次线程返回时更新的素数的排序列表。然后可以将其复制到par结构中,以便更快地进行主要检测(如果某个数字可以被现有的素数均匀分配,则该数字不是素数)。
注:我没有测试过这些例子,尽管它们应该足够接近以给你一般的想法。
终止线程猛烈是因为你一个坏主意,它可以让你的程序在一个糟糕的状态。如果你的线程运行在一个循环中,你可以设置一些线程可以测试的外部标志,并决定是否自行终止(这可以通过简单地退出thead函数来完成)。只要记住用互斥体来保护你的国旗。
还有一些其他的模式,你可能想看看。您可能想要将您的素数范围放入队列中。每个工作线程然后可以将值从队列中拉出并执行搜索。通过这种方式,您可以平均分配负载而不会终止并重新启动线程。
你的黄金搜索算法效率极其低下,但我想现在谈论..
线程:
设置为每个线程作业队列..杀/终止短的生活主题,尤其是对主要发现听起来像一个非常糟糕的主意。
避免争用。请勿对primes.push(i);
使用锁定。为每个线程设置一个单独的队列,并将结果推送到那里。当线程完成范围,然后输入关键部分并合并结果。这样你几乎不会锁定。
为每个线程设置一个作业队列会更有意义。杀死/终止短线程,特别是对于主要发现听起来像是一个非常糟糕的主意。你是否仅仅将它作为线程练习? – 2012-02-27 22:59:36
是的,我今天只是看着线程,这是一个练习 – 2012-02-27 23:06:50
运行一个多线程筛看起来似乎是一个更有趣的练习。 – 2012-02-27 23:39:21