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);
	}
};
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值