Code Monkey home page Code Monkey logo

threadpool's Issues

addThread 工作线程函数97行99行

                  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++;
}

由上述代码设计可见,子线程结束的方式有两种(代码中 /******/处):

  1. 第一处,当_run为false,即线程池析构时立刻停止。(这种方式结果为,当线程池析构时立刻回收所有子进程,丢弃任务队列中所有未完成任务)
  2. 第二处,要求_run为false且任务队列为空。(这种方式结果为,当线程池析构时,先完成剩下的所有任务再结束,个人认为这种设计更符合直觉)

两种方式混合使会用导致:当线程池析构时,如果任务队列还有任务,那么所有子线程会被唤醒,执行一次任务后被回收。既没有立即回收线程,也没有完全清空任务队列,就感觉挺迷惑的,如果要修改,应该使得子线程返回方式统一

  1. 要么将/****** 2/处的&&_tasks.empty()去掉,这样也就符合上面所说的第一种情况
  2. 要么将/****** 1/处的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
}

运行结果:
image

其实类似上面这种情况发生的概率其还挺大,只要当线程池析构的时候,任务队列中还有任务就会出现这种情况,最后会导致这种迷惑行为。
其实我猜想,作者设计那个while(_run)的判定可能是为了让线程池析构的时候,commit函数无法再创建新的子进程,但我觉得就算创建了其实也问题不大,反正可以在后面返回,不会产生不好的结果。

综上所述,我建议把第90行改成while(true)

(不知道我分析的对不对...,或者作者这样设计有别的用意?)

‘bind’ was not declared in this scope

大佬,我在本地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{...}) 放到队列后面

疑惑

当任务量大于线程数吗,且当前线程没有空闲的,会有问题吗

关于自动释放线程的一个问题

作者您好, 您的线程池的代码第132行中提到了关于自动释放线程的问题.

if (_idlThrNum>0 && _pool.size() > _initSize) //支持自动释放空闲线程,避免峰值过后大量空闲线程
						return;

这段代码中, 您选择如果当前存在空闲线程时就会主动关闭线程. 但是这里我认为其存在一定的问题, 因为线程池中虽然该线程已经关闭, 但是仍然该线程的资源仍然在_pool中, 那么在后续的工作中, 该线程资源将不会被再次使用. 这里会不会造成一定的资源浪费的问题.

举一个比较极端的例子, 假设我吧最大线程数开到了1e5, 然后瞬间增加了1e6个任务, 这些任务都完成了之后, 显然这里会出现1e5个空闲线程, 根据这里的机制, 我们会结束前面申请的所有的线程. 但是这里开的1e5个线程仍然是占据这_pool的内存的. 如果我经常性的重复上述过程, 那显然就会造成_pool的大小越来越大, 显然这是不合理的.

请问作者, 是否已经存在一些针对该问题的解决机制(我粗心没看到).

thread pool 与 numactl 同时使用,出现捕获不到commit的tasks

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

在只剩下一个空闲线程时,多个任务同时commit, 会发生什么?

这是一个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.

编译不过

error C2447: “{”: 缺少函数标题(是否是老式的形式表?)
“threadpool”: 不是“std”的成员

C++ multi-thread call python

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

单例模式

上面所设计的线程池类,在工程中我们实例化一个对象就够了,是否可以设计为单例模式

发现一个bug

理论上线程被释放之后——pool.size()应该减少一个,但是代码中没有删除_pool中元素的操作导致_pool的大小只增不减,这时如果输入压力增大线程数会因为达到上限而不开辟新线程,极端情况下甚至会导致线程清零(因为删除操作不使计数器减少导致"_pool.size() > _initSize"失效)从而没有线程执行新任务。

发现一个BUG

如果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;
}

`

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.