gpt4 book ai didi

c++ - Windows API 线程池简单示例

转载 作者:可可西里 更新时间:2023-11-01 13:30:28 30 4
gpt4 key购买 nike

[编辑:感谢 MSalters 的回答和 Raymond Chen 对 InterlockedIncrement vs EnterCriticalSection/counter++/LeaveCriticalSection 的回答,问题已解决,下面的代码可以正常工作。这应该提供一个在 Windows 中使用线程池的有趣的简单示例]

我没能找到以下任务的简单示例。例如,我的程序需要将巨大的 std::vector 中的值递增 1,因此我想并行执行此操作。它需要在程序的整个生命周期中多次这样做。我知道如何在每次调用例程时使用 CreateThread 来做到这一点,但我无法通过 ThreadPool 摆脱 CreateThread。

这是我的做法:

class Thread {
public:
Thread(){}
virtual void run() = 0 ; // I can inherit an "IncrementVectorThread"
};
class IncrementVectorThread: public Thread {
public:
IncrementVectorThread(int threadID, int nbThreads, std::vector<int> &vec) : id(threadID), nb(nbThreads), myvec(vec) { };

virtual void run() {
for (int i=(myvec.size()*id)/nb; i<(myvec.size()*(id+1))/nb; i++)
myvec[i]++; //and let's assume myvec is properly sized
}
int id, nb;
std::vector<int> &myvec;
};

class ThreadGroup : public std::vector<Thread*> {
public:
ThreadGroup() {
pool = CreateThreadpool(NULL);
InitializeThreadpoolEnvironment(&cbe);
cleanupGroup = CreateThreadpoolCleanupGroup();
SetThreadpoolCallbackPool(&cbe, pool);
SetThreadpoolCallbackCleanupGroup(&cbe, cleanupGroup, NULL);
threadCount = 0;
}
~ThreadGroup() {
CloseThreadpool(pool);
}
PTP_POOL pool;
TP_CALLBACK_ENVIRON cbe;
PTP_CLEANUP_GROUP cleanupGroup;
volatile long threadCount;
} ;


static VOID CALLBACK runFunc(
PTP_CALLBACK_INSTANCE Instance,
PVOID Context,
PTP_WORK Work) {

ThreadGroup &thread = *((ThreadGroup*) Context);
long id = InterlockedIncrement(&(thread.threadCount));
DWORD tid = (id-1)%thread.size();
thread[tid]->run();
}

void run_threads(ThreadGroup* thread_group) {
SetThreadpoolThreadMaximum(thread_group->pool, thread_group->size());
SetThreadpoolThreadMinimum(thread_group->pool, thread_group->size());

TP_WORK *worker = CreateThreadpoolWork(runFunc, (void*) thread_group, &thread_group->cbe);
thread_group->threadCount = 0;
for (int i=0; i<thread_group->size(); i++) {
SubmitThreadpoolWork(worker);
}
WaitForThreadpoolWorkCallbacks(worker,FALSE);
CloseThreadpoolWork(worker);
}

void main() {

ThreadGroup group;
std::vector<int> vec(10000, 0);
for (int i=0; i<10; i++)
group.push_back(new IncrementVectorThread(i, 10, vec));

run_threads(&group);
run_threads(&group);
run_threads(&group);

// now, vec should be == std::vector<int>(10000, 3);
}

所以,如果我理解得很好的话:
- 命令 CreateThreadpool 创建了一堆线程(因此,对 CreateThreadpoolWork 的调用很便宜,因为它不调用 CreateThread)
- 我可以拥有任意数量的线程池(如果我想为“IncrementVector”做一个线程池,为“DecrementVector”线程做一个线程池,我可以)。
- 如果我需要将我的“增量 vector ”任务分成 10 个线程,而不是调用 10 次 CreateThread,我创建一个“worker”,并使用相同的参数将它提交 10 次到 ThreadPool(因此,我需要线程回调中的 ID 以了解我的 std::vector 的哪一部分要递增)。在这里我找不到线程 ID,因为函数 GetCurrentThreadId() 返回线程的真实 ID(即,类似于 1528,而不是 0..nb_launched_threads 之间的东西)。

最后,我不确定我是否很好地理解了这个概念:如果我将 std::vector 分成 10 个线程,我真的需要一个 worker 而不是 10 个吗?

谢谢!

最佳答案

到最后一点为止,您大致说对了。

关于线程池的整个想法是你不关心它有多少个线程。你只是把很多工作扔到线程池里,让操作系统决定如何执行每个 block 。因此,如果您创建并提交 10 个 block ,操作系统可能会使用池中的 1 到 10 个线程。

你不应该关心那些线程标识。不要为线程 ID、线程的最小或最大数量或类似的东西而烦恼。

如果您不关心线程标识,那么您如何管理要更改 vector 的哪一部分?简单的。在创建线程池之前,将计数器初始化为零。在回调函数中,调用 InterlockedIncrement 来检索和递增计数器。对于每个提交的工作项,您将获得一个连续的整数。

关于c++ - Windows API 线程池简单示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8357955/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com