C++ C++;重用调用相同函数的线程向量
我想重用线程向量,这些线程使用不同的参数多次调用同一个函数。不需要写入(原子参数除外),因此不需要互斥。为了描述这个想法,我创建了一个并行化代码的基本示例,用于查找向量的最大值。显然有更好的方法来求向量的最大值,但是为了解释和避免深入了解我正在编写的实际代码的细节,我将使用这个愚蠢的示例 代码通过调用函数pFind查找向量的最大数目,该函数检查向量是否包含数字k(k用上限初始化)。如果执行,则停止执行,否则k将减少1并重复该过程 下面的代码生成一个线程向量,并行搜索向量中的k。问题在于,对于k的每个值,线程向量都会重新生成,并且每次新线程都会被连接。 生成线程向量并每次连接它们会带来我想要避免的开销 我想知道是否有一种方法可以只生成一次线程向量(一个池)并在新的执行中重用它们。任何其他加速提示将不胜感激C++ C++;重用调用相同函数的线程向量,c++,multithreading,c++14,threadpool,stdthread,C++,Multithreading,C++14,Threadpool,Stdthread,我想重用线程向量,这些线程使用不同的参数多次调用同一个函数。不需要写入(原子参数除外),因此不需要互斥。为了描述这个想法,我创建了一个并行化代码的基本示例,用于查找向量的最大值。显然有更好的方法来求向量的最大值,但是为了解释和避免深入了解我正在编写的实际代码的细节,我将使用这个愚蠢的示例 代码通过调用函数pFind查找向量的最大数目,该函数检查向量是否包含数字k(k用上限初始化)。如果执行,则停止执行,否则k将减少1并重复该过程 下面的代码生成一个线程向量,并行搜索向量中的k。问题在于,对于k的
void pFind(
vector<int>& a,
int n,
std::atomic<bool>& flag,
int k,
int numTh,
int val
) {
int i = k;
while (i < n) {
if (a[i] == val) {
flag = true;
break;
} else
i += numTh;
}
}
int main() {
std::atomic<bool> flag;
flag = false;
int numTh = 8;
int val = 1000;
int pos = 0;
while (!flag) {
vector<thread>threads;
for (int i = 0; i < numTh; i++){
thread th(&pFind, std::ref(a), size, std::ref(flag), i, numTh, val);
threads.push_back(std::move(th));
}
for (thread& th : threads)
th.join();
if (flag)
break;
val--;
}
cout << val << "\n";
return 0;
}
void pFind(
向量&a,
int n,
标准::原子和标志,
int k,
整数,
int val
) {
int i=k;
而(i cout您的代码具有竞争条件:bool
不是原子类型,因此多个线程并发写入不安全。您需要使用或
为了回答您的问题,您正在为循环的每次迭代重新创建线程
向量,您可以通过将其声明移到循环体之外来避免这种情况。重用线程本身是一个更为复杂的主题,很难正确或简洁地描述
vector<thread> threads;
threads.reserve(numTh);
while (!flag) {
for (size_t i = 0; i < numTh; ++i)
threads.emplace_back(pFind, a, size, flag, i, numTh, val);
for (auto &th : threads)
th.join();
threads.clear();
}
向量线程;
线程保留(numTh);
while(!flag){
对于(尺寸i=0;i
无法分配不同的执行功能(关闭)在构造后对std::thread
。这通常适用于所有线程抽象,尽管通常实现会尝试在内部记忆或缓存较低级别的抽象,以使线程分叉和连接速度更快,因此仅构造新线程是可行的。在系统编程界,关于是否创建新线程存在争议read应该是非常轻量级的,或者客户端是否应该像fork线程那样频繁地被写入(考虑到这种情况已经持续了很长时间,应该很清楚,这涉及到很多折衷)
还有很多其他的抽象,它们试图做你真正想做的事情。它们的名字有“线程池”、“任务执行者”(或者只是“执行者”)和“未来”它们都倾向于通过创建一组线程(通常与系统中硬件内核的数量有关)映射到线程,然后让每个线程循环并查找请求
如注释所示,您自己这样做的主要方式是让线程具有顶级循环,该循环接受执行请求,处理它们,然后发布结果。要做到这一点,您需要使用其他同步方法,如互斥体和条件变量。如果有,通常这样做会更快很多请求和请求都不是非常大
标准C++的并发支持是一件好事,它也相当缺乏现实世界高性能的工作。类似的是更为工业化的解决方案。
< p>通过拼凑来自不同在线搜索的一些代码,下面的工作,但是是<>强>不快> <强> >它在while循环的每次迭代中重新生成线程
也许有人可以对这种方法发表评论
下面的类描述线程池
class ThreadPool {
public:
ThreadPool(int threads) : shutdown_(false){
threads_.reserve(threads);
for (int i = 0; i < threads; ++i)
threads_.emplace_back(std::bind(&ThreadPool::threadEntry, this, i));
}
~ThreadPool(){
{
// Unblock any threads and tell them to stop
std::unique_lock<std::mutex>l(lock_);
shutdown_ = true;
condVar_.notify_all();
}
// Wait for all threads to stop
std::cerr << "Joining threads" << std::endl;
for (auto & thread : threads_) thread.join();
}
void doJob(std::function<void(void)>func){
// Place a job on the queu and unblock a thread
std::unique_lock<std::mutex>l(lock_);
jobs_.emplace(std::move(func));
condVar_.notify_one();
}
void threadEntry(int i){
std::function<void(void)>job;
while (1){
{
std::unique_lock<std::mutex>l(lock_);
while (!shutdown_ && jobs_.empty()) condVar_.wait(l);
if (jobs_.empty()){
// No jobs to do and we are shutting down
std::cerr << "Thread " << i << " terminates" << std::endl;
return;
}
std::cerr << "Thread " << i << " does a job" << std::endl;
job = std::move(jobs_.front());
jobs_.pop();
}
// Do the job without holding any locks
job();
}
}
};
类线程池{
公众:
线程池(int线程):关闭(false){
螺纹预留(螺纹);
对于(int i=0;i 通常,您编写的任务处理器使用从队列中提取的参数执行给定任务;主线程填充共享的参数队列,工作人员根据需要从中提取新工作。您需要访问队列的互斥体,但前提是插入和提取工作项的成本较低(使用.front()
的std::move
以避免在.pop()
之后进行复制构造以删除该项,而所有其他工作都不在互斥锁的控制范围内,或者如果std::move
仍然非常昂贵,存储和检索unique\u ptr
或类似内容),锁争用应该是最小的
void pFind(
vector<int>& a,
int n,
std::atomic<bool>& flag,
int k,
int numTh,
int val,
std::atomic<int>& completed) {
int i = k;
while (i < n) {
if (a[i] == val) {
flag = true;
break;
} else
i += numTh;
}
completed++;
}
int main() {
std::atomic<bool> flag;
flag = false;
int numTh = 8;
int val = 1000;
int pos = 0;
std::atomic<int> completed;
completed=0;
ThreadPool p(numThreads);
while (!flag) {
for (int i = 0; i < numThreads; i++) {
p.doJob(std::bind(pFind, std::ref(a), size, std::ref(flag), i, numTh, val, std::ref(completed)));
}
while (completed < numTh) {}
if (flag) {
break;
} else {
completed = 0;
val--;
}
}
cout << val << "\n";
return 0;
}