首先,了解一下该系统重要的数据类型.
1. FQueuedThreadPool:虚基类,队列线程池, FQueuedThreadPoolBase继承自FQueuedThreadPool,
FQueuedThreadPoolBase维护了一个TArray<IQueuedWork*> QueuedWork(需要被执行的工作), TArray<FQueuedThread*> AllThreads(所 有的线程)
TArray<FQueuedThread*> QueuedThreads ( 空闲的线程)
2. FQueuedThread继承自FRunnable, 代表了线程的执行体,但并非线程本身。一个线程创建后将会执行FRunnable的Run()函数.
3. FRunnableThread代表了线程本身,该类会派生出平台相关的子类,win32下对应的是FRunnableThreadWin,建议读者看看某个平台下的具体实 现。在创建时需要指定一个FRunnable,用于线程执行。
4. FEvent: 虚基类,提供了事件操作的接口,用于线程的激活挂起, 该类会派生出平台相关的子类,win32下对应的是FEventWin。
两个重要接口: Wait()将会使线程挂起,Trigger()将会激活线程,配对使用。
了解了相关的数据类型后,可以开始剖析多线程任务系统的流程了,先从AsyncWork.h开始。
AsyncWork.h中有几个使用多线程任务系统的例子,先来了解我们如何使用多线程来执行任务。
大体流程如下:
先自定义一个任务类,用于执行我们的任务
class ExampleAutoDeleteAsyncTask : public FNonAbandonableTask
{
friend class FAutoDeleteAsyncTask<ExampleAutoDeleteAsyncTask>;
//核心接口, 这里便是任务的具体执行内容.
void DoWork()
{
}
};
//任务模板类,其作用是托管我们自定义的任务类,如执行完任务后自动销毁.
template<typename TTask>
class FAutoDeleteAsyncTask : private IQueuedWork
{
TTask Task;
//核心接口,任务的执行从这里开始, 参数bForceSynchronous = false表示异步执行
void Start(bool bForceSynchronous)
{
FPlatformMisc::MemoryBarrier();
FQueuedThreadPool* QueuedPool = GThreadPool;
if (bForceSynchronous)
{
QueuedPool = 0;
}
if (QueuedPool)
{ //异步执行,把自身(IQueuedWork)添加到队列线程池中去执行
QueuedPool->AddQueuedWork(this);
}
else
{
// 同步执行,直接调用Task.DoWork();
DoWork();
}
}
void DoWork()
{
Task.DoWork();
delete this;
}
virtual void DoThreadedWork()
{
DoWork();
}
void StartSynchronousTask()
{
Start(true);
}
void StartBackgroundTask()
{
Start(false);
}
};
//使用多线程任务的例子
void Example()
{
//异步执行Task
(new FAutoDeleteAsyncTask<ExampleAutoDeleteAsyncTask>(5)->StartBackgroundTask();
//同步执行Task
(new FAutoDeleteAsyncTask<ExampleAutoDeleteAsyncTask>(5)->StartSynchronousTask();
}
如何使用多线程来执行任务已经展示完了,就是这么easy!
接下来分析多线程来执行流程,重点是QueuedPool实例化时做了什么 以及 QueuedPool->AddQueuedWork(this);
我们先看一下QueuedPool实例是如何得到的:QueuedPool = GThreadPool; 而GThreadPool是在引擎初始化的时候创建的,代码在launchEngineLoop文件里,如下:
if (FPlatformProcess::SupportsMultithreading())
{
//可以看出,只有在系统支持多线程的情况下才会创建GThreadPool
GThreadPool = FQueuedThreadPool::Allocate();
int32 NumThreadsInThreadPool = FPlatformMisc::NumberOfWorkerThreadsToSpawn();
verify(GThreadPool->Create(NumThreadsInThreadPool));
。。。。。。
}
GThreadPool 创建之后便调用了它的Create
GThreadPool->Create(InNumQueuedThreads)
{
// 主要是创建N个线程
for (uint32 Count = 0; Count < InNumQueuedThreads && bWasSuccessful == true; Count++)
{
FQueuedThread* pThread = new FQueuedThread();
if (pThread->Create(this,StackSize,ThreadPriority) == true)
{
QueuedThreads.Add(pThread);
AllThreads.Add(pThread);
}
}
。。。。。。。
}
在FQueuedThread的Create接口里,创建了平台相关的线程和系统事件,该线程的执行体是FQueuedThread的Run函数.
FQueuedThread::Create()
{
DoWorkEvent = FPlatformProcess::GetSynchEventFromPool();
Thread = FRunnableThread::Create(this, *PoolThreadName, InStackSize, ThreadPriority, FPlatformAffinity::GetPoolThreadMask());
。。。。。。。
}
Create()之后,线程执行了Run函数。
然而,在线程初始化之后,Run会一直处在while循环中,不断等待DoWorkEvent->Trigger()
FQueuedThread::Run()
{
bool bContinueWaiting = true;
//在这个循环里,Wait()使线程不断挂起.
while( bContinueWaiting )
{
bContinueWaiting = !DoWorkEvent->Wait( 10 );
}
。。。。。。。
}
讲到这里,先小结一下,实例化GThreadPool实际上也就是创建了N个线程,每个线程都处于一个不断挂起,醒来,挂起的循环中。
这N个线程用于执行异步任务,用户需要执行自定义的任务可以参考AsyncWork.h的例子。
那么,一定在某个地方会调用DoWorkEvent->Trigger()使Run能够跳出死循环,执行后面的代码。
聪明的读者一定想到了在前面提到的 (GThreadPool等于QueuedPool)QueuedPool->AddQueuedWork(this), 没错,就是它,在AddQueuedWork里面便间接调用了DoWorkEvent->Trigger()
QueuedPool->AddQueuedWork(InQueuedWork)
{
//取出一个空闲的线程执行InQueuedWork,没有则添加到QueuedWork中
if (QueuedThreads.Num() > 0)
{
int32 Index = 0;
Thread = QueuedThreads[Index];
QueuedThreads.RemoveAt(Index);
}
if (Thread != nullptr)
{
//DoWork实际上只是触发了event,从而激活等待的线程
Thread->DoWork(InQueuedWork);
}
else
QueuedWork.Add(InQueuedWork);
}
FQueuedThread::DoWork(IQueuedWork* InQueuedWork)
{
DoWorkEvent->Trigger();
}
调用DoWorkEvent->Trigger()之后,线程的Run()得以继续执行,接下来的工作便是不断取出Task来执行.
FQueuedThread::Run()
{
。。。。。。。。
IQueuedWork* LocalQueuedWork = QueuedWork
while (LocalQueuedWork)
{
//最终会执行Task.DoWork();
LocalQueuedWork->DoThreadedWork();
//取出下一个待执行的task或者把该FQueuedThread添加回空闲线程池中
LocalQueuedWork = OwningThreadPool->ReturnToPoolOrGetNextJob(this);
}
}
至此,整个多线程任务的执行流程便分析完了!
---------------------
作者:子轩Q
来源:CSDN
原文:https://blog.csdn.net/tuanxuan123/article/details/52780629
版权声明:本文为博主原创文章,转载请附上博文链接!