關係
workerThread和jobProvider是多對一的關係。
- workerThread.m_curJobProvider可以索引到workerThread當前所對應的jobProvider
- jobProvider.m_ownerBitmap可以索引到其名下的所有workerThread
jobProvider通過重寫findJob函數負責提供工作,通過tryWakeOne()函數來喚醒一個休眠線程來執行,而workerThread在主運行函數內部通過調用findJob函數執行工作。
流程圖
jobProvider
// Frame level job providers. FrameEncoder and Lookahead derive from
// this class and implement findJob()
class JobProvider
{
public:
ThreadPool* m_pool; // 所在的thread pool
sleepbitmap_t m_ownerBitmap; // 當前jobProvider所擁有的線程的位圖
int m_jpId; // job id
int m_sliceType; // slice類型,一定意義上代表當前jobProvider的優先級
bool m_helpWanted; // 是否需要幫助
bool m_isFrameEncoder; /* rather ugly hack, but nothing better presents itself */
JobProvider()
: m_pool(NULL)
, m_ownerBitmap(0)
, m_jpId(-1)
, m_sliceType(INVALID_SLICE_PRIORITY)
, m_helpWanted(false)
, m_isFrameEncoder(false)
{}
virtual ~JobProvider() {}
// Worker threads will call this method to perform work
// jobProvider提供給workerThread執行的作業
virtual void findJob(int workerThreadId) = 0;
// Will awaken one idle thread, preferring a thread which most recently
// performed work for this provider.
// 嘗試喚醒一個sleeping thread來執行當前的job
void tryWakeOne() {
// 首先在jobProvider名下的線程裏找,若無則在所有線程池裏找
int id = m_pool->tryAcquireSleepingThread(m_ownerBitmap, ALL_POOL_THREADS);
//獲取線程失敗,當前jobProvider需要幫助
if (id < 0)
{
m_helpWanted = true;
return;
}
// 若獲取成,則得到獲取的work thread
WorkerThread& worker = m_pool->m_workers[id];
//若獲取的WorkerThread的jobProvider不是當前job,建立他們的關係
if (worker.m_curJobProvider != this) /* poaching */
{
// 構造當前線程的位圖
sleepbitmap_t bit = (sleepbitmap_t)1 << id;
// 在當前線程的jobProvider所擁有的線程位圖中,取消掉當前線程的擁有
SLEEPBITMAP_AND(&worker.m_curJobProvider->m_ownerBitmap, ~bit);
// 將當前jobProvider給分配的獲取的workThread
worker.m_curJobProvider = this;
// 在當前jobProvider所擁有的線程位圖,勾上當前線程
SLEEPBITMAP_OR(&worker.m_curJobProvider->m_ownerBitmap, bit);
}
// 喚醒獲取的線程來執行job
worker.awaken();
}
};
workerThread
/*
工作線程
*/
class WorkerThread : public Thread
{
private:
ThreadPool& m_pool; // 線程所隸屬的線程池
int m_id; // 線程id
Event m_wakeEvent; // 生產者消費者模型,這裏把線程當作臨界資源
WorkerThread& operator =(const WorkerThread&);
public:
JobProvider* m_curJobProvider;
BondedTaskGroup* m_bondMaster;
// 構造函數,初始化當前線程的隸屬線程池和線程id
WorkerThread(ThreadPool& pool, int id) : m_pool(pool), m_id(id) {}
virtual ~WorkerThread() {}
// 這裏把線程當作臨界資源,喚醒當前線程
void awaken() { m_wakeEvent.trigger(); }
// 線程運行的主函數
void threadMain() {
THREAD_NAME("Worker", m_id);
#if _WIN32
// 給當前線程設置優先級爲BELOW_NORMAL
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
#else
__attribute__((unused)) int val = nice(10);
#endif
// 將當前線程歸屬到m_pool線程池中
m_pool.setCurrentThreadAffinity();
// 構造當前線程的位圖
sleepbitmap_t idBit = (sleepbitmap_t)1 << m_id;
// 將所在線程池的第一個jobPovider分配給當前線程,與之對應
m_curJobProvider = m_pool.m_jpTable[0];
m_bondMaster = NULL;
// 在線程對應的jobProvider的線程位圖上勾選本線程
SLEEPBITMAP_OR(&m_curJobProvider->m_ownerBitmap, idBit);
// 在線程池sleep位圖上添加本線程,並睡眠當前線程,
// 等待jobProvider進行awaken
SLEEPBITMAP_OR(&m_pool.m_sleepBitmap, idBit);
m_wakeEvent.wait();
// 若線程池活躍,開始佔用線程資源,執行線程主函數循環
// 這裏可以看到,線程的整個生命週期與線程池同步,
// 線程池活躍則線程存在(可能在工作也可能在sleep)
// 線程池失活則線程消亡
while (m_pool.m_isActive)
{
// 先檢查當前線程是否被徵用,若被徵用到m_bondMaster則執行task
if (m_bondMaster)
{
// 執行m_bondMaster的task
m_bondMaster->processTasks(m_id);
// 執行完畢線程退出,m_exitedPeerCount累加
m_bondMaster->m_exitedPeerCount.incr();
// 置空
m_bondMaster = NULL;
}
// 按照優先級,不斷的執行jobProvider的工作,
// 直到沒有需要help的jobProvider爲止,
do
{
/* do pending work for current job provider
jobProvider帶着當前workerThread去執行工作 */
m_curJobProvider->findJob(m_id);
/* if the current job provider still wants help, only switch to a
* higher priority provider (lower slice type). Else take the first
* available job provider with the highest priority
#define X265_TYPE_AUTO 0x0000 // Let x265 choose the right type
#define X265_TYPE_IDR 0x0001
#define X265_TYPE_I 0x0002
#define X265_TYPE_P 0x0003
#define X265_TYPE_BREF 0x0004 // Non-disposable B-frame
#define X265_TYPE_B 0x0005 */
// 若當前jobProvider需要幫助則返回其sliceType作爲優先級,否則優先級最低
// 也就是說優先級IDR > I > P > BREF > B > INVALID_SLICE_PRIORITY + 1
int curPriority = (m_curJobProvider->m_helpWanted) ? m_curJobProvider->m_sliceType :
INVALID_SLICE_PRIORITY + 1;
// 遍歷workerThread所在線程池的所有jobProvider
// 尋找需要幫助且優先級高於自己的jobProvider的最高優先級nextProvider
int nextProvider = -1;
for (int i = 0; i < m_pool.m_numProviders; i++)
{
if (m_pool.m_jpTable[i]->m_helpWanted &&
m_pool.m_jpTable[i]->m_sliceType < curPriority)
{
nextProvider = i;
curPriority = m_pool.m_jpTable[i]->m_sliceType;
}
}
// 若找到了,且不是當前線程對應的jobProvider,
// 則將該jobProvider與當前workerThread綁定關係
if (nextProvider != -1 && m_curJobProvider != m_pool.m_jpTable[nextProvider])
{
// 在當前wokerThread對應的jobProvider的線程位圖中取消勾選當前線程
SLEEPBITMAP_AND(&m_curJobProvider->m_ownerBitmap, ~idBit);
// 將找到的jobProvider與當前線程綁定
m_curJobProvider = m_pool.m_jpTable[nextProvider];
// 在新的jobProvider線程位圖中勾選當前線程
SLEEPBITMAP_OR(&m_curJobProvider->m_ownerBitmap, idBit);
}
} while (m_curJobProvider->m_helpWanted);
/* While the worker sleeps, a job-provider or bond-group may acquire this
* worker's sleep bitmap bit. Once acquired, that thread may modify
* m_bondMaster or m_curJobProvider, then waken the thread
* 在線程池sleep位圖上添加本線程,並睡眠當前線程
* 等待jobProvider進行awaken */
SLEEPBITMAP_OR(&m_pool.m_sleepBitmap, idBit);
m_wakeEvent.wait();
}
// 線程退出,在線程池的sleep位圖中勾選當前線程
SLEEPBITMAP_OR(&m_pool.m_sleepBitmap, idBit);
}
};