一.说明
Tensorflow使用的线程池来自Eigen库,Eigen源码可在eigen: eigen eigen - Gitee.com 下载,
其中线程池相关代码在eigen-3.4\unsupported\Eigen\CXX11\src\ThreadPool,如下图:
是的你没看错,只有.h,类的方法都在头文件里实现了。Eigen线程池一大亮点是少锁设计(较少使用互斥锁,避免因为频繁使用锁带来额外开销,因此叫“NonBlocking”),下文详细介绍
二.代码解析
1.核心代码文件简介
EventCount.h
用于条件等待,使用了“少锁设计”。该类的实现借鉴了荷兰数学家Dekker提出的互斥算法(参考:临界区互斥实现:Dekker互斥算法 - 知乎),实现原理较复杂,本文不展开。
RunQueue.h
任务队列,是一种双端队列(deque,关于双端队列的介绍见:https://zhuanlan.zhihu.com/p/387108933),使用了“少锁设计”。操作队列的头部时(任务从头部入队/出队)不需要互斥锁(线程池的调度逻辑会保证队列的头部只会被Queue所属的线程操作),但操作队列尾部时使用了互斥锁。
主要方法有PushFront(队列头部插入任务)、PopFront(队列头部弹出任务)、PushBack和PopBack。
ThreadPoolInterface.h
定义了一个抽象类,给具体的线程池类继承用的。
class ThreadPoolInterface {
public:
// Submits a closure to be run by a thread in the pool.
virtual void Schedule(std::function<void()> fn) = 0;
// Submits a closure to be run by threads in the range [start, end) in the
// pool.
virtual void ScheduleWithHint(std::function<void()> fn, int /*start*/,
int /*end*/) {
// Just defer to Schedule in case sub-classes aren't interested in
// overriding this functionality.
Schedule(fn);
}
// If implemented, stop processing the closures that have been enqueued.
// Currently running closures may still be processed.
// If not implemented, does nothing.
virtual void Cancel() {}
// Returns the number of threads in the pool.
virtual int NumThreads() const = 0;
// Returns a logical thread index between 0 and NumThreads() - 1 if called
// from one of the threads in the pool. Returns -1 otherwise.
virtual int CurrentThreadId() const = 0;