x265多線程-jobProvider/workerThread

關係

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);
	}
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章