lzpong / threadpool Goto Github PK
View Code? Open in Web Editor NEWbased on C++11 , a mini threadpool , accept variable number of parameters 基于C++11的线程池,简洁且可以带任意多的参数
Home Page: http://www.cnblogs.com/lzpong/p/6397997.html
based on C++11 , a mini threadpool , accept variable number of parameters 基于C++11的线程池,简洁且可以带任意多的参数
Home Page: http://www.cnblogs.com/lzpong/p/6397997.html
大佬,尝试了一下打开自动增长的THREADPOOL_AUTO_GROW的宏,GDB调试的时候发现AUTO_GROW的线程大多时候创建完就退出了,有啥思路吗?
有两个问题想请教:1.有没有别的写法
2.我使用的方法为:std::packaged_task<ResType()>task(forward(f), std::forward(args)...) ); 就是没给makeshared 为什么会报错呢
unique_lock<mutex> lock{ _lock };
96 _task_cv.wait(lock, [this]{
97 return !_run || !_tasks.empty();
98 }); // wait 直到有 task
99 if (!_run && _tasks.empty())
100 return;
_tasks.empty() 为什么要判断两次呢
97 行是队列不为空就继续等待
99 行不也是队列不为空就往下
{”: 缺少函数标题(是否是老式的形式表?)
子线程工作函数如下
while (_run) /****** 1/
{
Task task; // 获取一个待执行的 task
{
// unique_lock 相比 lock_guard 的好处是:可以随时 unlock() 和 lock()
unique_lock<mutex> lock{ _lock };
_task_cv.wait(lock, [this]{
return !_run || !_tasks.empty();
}); // wait 直到有 task
if (!_run && _tasks.empty()) /****** 2/
return;
task = move(_tasks.front()); // 按先进先出从队列取一个 task
_tasks.pop();
}
_idlThrNum--;
task();//执行任务
_idlThrNum++;
}
由上述代码设计可见,子线程结束的方式有两种(代码中 /******/处):
两种方式混合使会用导致:当线程池析构时,如果任务队列还有任务,那么所有子线程会被唤醒,执行一次任务后被回收。既没有立即回收线程,也没有完全清空任务队列,就感觉挺迷惑的,如果要修改,应该使得子线程返回方式统一
&&_tasks.empty()
去掉,这样也就符合上面所说的第一种情况while (_run)
改为while(true)
,这样也就符合上面所说的第二种情况其实我认为作者是想写出第二种情况的代码的。如果这样的话/****** 1/处可能是个小bug,这会导致线程池在析构时可能会出错。
下面有个小例子可以证明我的想法。这是一个非常简单的多线程累加器,当却会导致未定义行为。
注意,该代码我在我自己的mac上用clang编译是没有问题的。但在centos上用g++编译就会出现问题,输出的结果是0,原因是线程池中的4个子线程在执行while (_run)
执行前,线程池就进行析构(将_run修改为false),导致子线程直接返回,抛弃任务队列中的所有的任务,导致结果为0。(这里有个有意思的现象,我在centos上使用gdb调试来运行,结果又是正确的)可以试着在服务器上运行一下这个代码,看会不会出现相同的情况,我的环境是:CentOS Linux release 7.9.2009 (Core)
、 g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44)
#include "ThreadPool.h"
int x = 0;
std::mutex mu;
void fun() {
std::unique_lock<std::mutex> m{mu};
for(int i = 0; i < 10000; i++)
x++;
}
int main() {
{
std::threadpool tp;
for(int i = 0; i < 100; i++)
tp.commit(fun);
}//该作用域为了析构tp,确保线程池中所有线程已经运行完
printf("%d\n", x); // 结果应该为 1000000
}
其实类似上面这种情况发生的概率其还挺大,只要当线程池析构的时候,任务队列中还有任务就会出现这种情况,最后会导致这种迷惑行为。
其实我猜想,作者设计那个while(_run)
的判定可能是为了让线程池析构的时候,commit
函数无法再创建新的子进程,但我觉得就算创建了其实也问题不大,反正可以在后面返回,不会产生不好的结果。
while(true)
。(不知道我分析的对不对...,或者作者这样设计有别的用意?)
你好。
bind: .commit(std::bind(&Dog::sayHello, &dog))
这个好像不能带参数。
大佬,我在本地CLION编译运行没有问题,在服务器上编译出现下面这个错误,我在网上找不到解决方法,你知道是什么愿意吗?
In file included from test.cpp:7:0:
util/threadpool.h: In instantiation of ‘std::future<decltype (f(std::threadpool::commit::args ...))> std::threadpool::commit(F&&, Args&& ...) [with F = void (&)(int); Args = {int}; decltype (f(std::threadpool::commit::args ...)) = void]’:
test.cpp:21:50: required from here
util/threadpool.h:59:17: error: ‘bind’ was not declared in this scope
bind(forward(f), forward(args)...)
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
util/threadpool.h:59:17: note: suggested alternative: ‘rand’
bind(forward(f), forward(args)...)
~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
rand
In file included from test.cpp:7:0:
util/threadpool.h: In instantiation of ‘struct std::threadpool::commit(F&&, Args&& ...) [with F = void (&)(int); Args = {int}; decltype (f(std::threadpool::commit::args ...)) = void]::<lambda()>’:
util/threadpool.h:64:13: required from ‘std::future<decltype (f(std::threadpool::commit::args ...))> std::threadpool::commit(F&&, Args&& ...) [with F = void (&)(int); Args = {int}; decltype (f(std::threadpool::commit::args ...)) = void]’
test.cpp:21:50: required from here
util/threadpool.h:64:29: error: using invalid field ‘std::threadpool::commit(F&&, Args&& ...)::<lambda()>::’
_tasks.emplace(task{ // push(Task{...}) 放到队列后面
rt
#ifndef THREADPOOL_AUTO_GROW
mutex _lockGrow; //线程池增长同步锁
30行,需要修改下 ifndef
当任务量大于线程数吗,且当前线程没有空闲的,会有问题吗
作者您好, 您的线程池的代码第132行中提到了关于自动释放线程的问题.
if (_idlThrNum>0 && _pool.size() > _initSize) //支持自动释放空闲线程,避免峰值过后大量空闲线程
return;
这段代码中, 您选择如果当前存在空闲线程时就会主动关闭线程. 但是这里我认为其存在一定的问题, 因为线程池中虽然该线程已经关闭, 但是仍然该线程的资源仍然在_pool
中, 那么在后续的工作中, 该线程资源将不会被再次使用. 这里会不会造成一定的资源浪费的问题.
举一个比较极端的例子, 假设我吧最大线程数开到了1e5, 然后瞬间增加了1e6个任务, 这些任务都完成了之后, 显然这里会出现1e5个空闲线程, 根据这里的机制, 我们会结束前面申请的所有的线程. 但是这里开的1e5个线程仍然是占据这_pool
的内存的. 如果我经常性的重复上述过程, 那显然就会造成_pool
的大小越来越大, 显然这是不合理的.
请问作者, 是否已经存在一些针对该问题的解决机制(我粗心没看到).
线程被互斥执行,并没有并发,达不到多线程的意义。
“resource unavailable try again:”
为何仅支持静态的成员函数,普通的成员函数为何不支持呢?
你好,有点不理解的地方,idlThrNum是原子的,为啥修改时还要加锁?
thread pool 与 numactl 同时使用,出现捕获不到commit的tasks
例如 OMP_NUM_THREADS=27 numactl --localalloc --physcpubind=0-26 捕获不到
只有 当OMP_NUM_THREADS 大于 physcpubind的时候 才可以捕获到
例如OMP_NUM_THREADS=27 numactl --localalloc --physcpubind=0-25
这是一个race condition的情况,
#ifdef THREADPOOL_AUTO_GROW
if (_idlThrNum < 1 && _pool.size() < THREADPOOL_MAX_NUM)
addThread(1);
#endif // !THREADPOOL_AUTO_GROW
_task_cv.notify_one(); // 唤醒一个线程执行
如果还有一个线程,多个commit同时发生,if 为 false, 直接执行notify, 而不会创建新的线程,有且仅有一个线程被唤醒,而且任务执行完也不判断任务队列的空与否,那么有的任务永远不会被执行。
另外,addThread里没有锁保护,下面的判断也会出错
void addThread(unsigned short size)
{
for (; _pool.size() < THREADPOOL_MAX_NUM && size > 0; --size)
同时调用的时候,可能poo.size 都满足,实际上已经超过max.
如题所示,向 namespace std 添加内容是 ub
error C2447: “{”: 缺少函数标题(是否是老式的形式表?)
“threadpool”: 不是“std”的成员
hi,do you know how to use C++ multi-thread call python,can you help me ,thanks a lot,you can leave your email or wechat account ,my is 18811396527
上面所设计的线程池类,在工程中我们实例化一个对象就够了,是否可以设计为单例模式
理论上线程被释放之后——pool.size()应该减少一个,但是代码中没有删除_pool中元素的操作导致_pool的大小只增不减,这时如果输入压力增大线程数会因为达到上限而不开辟新线程,极端情况下甚至会导致线程清零(因为删除操作不使计数器减少导致"_pool.size() > _initSize"失效)从而没有线程执行新任务。
第98行,在#ifndef AUTO_GROW 的时候使用了只有ifdef 时才会定义的_lockGrow锁,在不进行自增的时候会导致编译不通过吧,在进行自增的时候,这个锁就没意义了?
when idle thread too much,should reduce threads automatically
如果commit函数是static的,则无法执行,请问这是为什么呢?
代码:
`
static void test(int a, int b) {
printf("a = %d, b = %d\n", a, b);
}
int main() {
std::threadpool executor;
executor.commit(test, 10, 20);
return 0;
}
`
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.