UE4多线程任务系统详解

首先,了解一下该系统重要的数据类型.


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
版权声明:本文为博主原创文章,转载请附上博文链接!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/432182.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

UE4异步编程专题 - 线程池FQueuedThreadPool

1. FQueuedThreadPool & IQueuedWork FQueuedThreadPool是UE4中抽象出的线程池。线程池由若干个Worker线程&#xff0c;和一个同步队列构成。UE4把同步队列执行的任务抽象为IQueuedWork. 线程池的同步队列&#xff0c;就是一个IQueuedWork的队列了。借用wiki上线程池的图,…

UE4异步编程专题 - 多线程

专题的第二篇&#xff0c;我们聊聊UE4中的多线程的基础设施。UE4中最基础的模型就是FRunnable和FRunnableThread&#xff0c;FRunnable抽象出一个可以执行在线程上的对象&#xff0c;而FRunnableThread是平台无关的线程对象的抽象。后面的篇幅会详细讨论这些基础设施。 1. FRu…

坑爹的UICollectionView

最近用UICoolectionView的时候遇到一个很DT的问题&#xff0c;我往VC里加12个视图&#xff0c;结果显示成这样&#xff08;右边是期待的样子&#xff09;&#xff1a; 研究了一下午&#xff0c;终于发现了问题&#xff1a; interface FpLabelCell : UICollectionViewCellproper…

UE4异步编程专题 - TFunction

0. 关于这个专题 游戏要给用户良好的体验&#xff0c;都会尽可能的保证60帧或者更高的fps。一帧留给引擎的时间也不过16ms的时长&#xff0c;再除去渲染时间&#xff0c;留给引擎时间连10ms都不到&#xff0c;能做的事情是极其有限的。同步模式执行耗时的任务&#xff0c;时长…

Python用subprocess的Popen来调用系统命令

当我们须要调用系统的命令的时候&#xff0c;最先考虑的os模块。用os.system()和os.popen()来进行操作。可是这两个命令过于简单&#xff0c;不能完毕一些复杂的操作&#xff0c;如给执行的命令提供输入或者读取命令的输出&#xff0c;推断该命令的执行状态&#xff0c;管理多个…

7700装win7

1.可能不能安装版本太新的win7系统,会蓝屏 2.第一次重启后,系统会提示硬件太新,系统不支持,不用理会.可以用shiftF10,进入windows/system32/oobe目录,执行msoobe手动安装. 3.第一次进入系统后,尽早关闭系统更新,除了在控制面板中关闭,还要在services.msc中关闭windows update服…

UE4高级功能--初探超大无缝地图的实现LevelStream

LevelStream 实现超大无缝地图--官方文档学习 The Level Streaming feature makes it possible to load and unload map files into memory as well as toggle their visibility all during play. This makes it possible to have worlds broken up into smaller chunks so th…

Noip 2014酱油记+简要题解

好吧&#xff0c;day2T1把d默认为1也是醉了&#xff0c;现在只能期待数据弱然后怒卡一等线吧QAQDay0 第一次下午出发啊真是不错&#xff0c;才2小时左右就到了233&#xff0c;在车上把sao和fate补掉就到了 然后到宾馆之后&#xff0c;没wifi的生活就是惨啊QAQ 把空境补完就睡了…

一个取消事件的简单js例子(事件冒泡与取消默认行为)

先上代码&#xff1a; <div idouter onclickalert("我是outer")><div id"middle" onclickalert("我是middle")><div id"inner" onclickmyBubble(arguments[0]);alert("我是inner")><a onclickmyDefaul…

inside uboot (二) 启动流程

1. S3C6410 启动流程 1). 6410上电后&#xff0c;首先执行片内iROM的程序&#xff08;BL0&#xff09;&#xff0c;初始化时钟和看门狗等外围器件。 2). 然后把flash中头4K&#xff08;BL1&#xff09;的内容加载到片内的SRAM中执行。 3). 在SRAM中执行的BL1&#xff0c;初始…

你理解我的意思么?

在最近一次的电话会议里, 某leader前后说了十来句, "你理解我的意思么?". 说实话, 有些我都没理解, 不过我听到的大家的答复都是"理解!", "明白!". Leader问这样的问题, 期望是得到对方给你反馈, 结果大部分人不会直接对上级说, "是的,我不…

inside uboot (三) 异常向量表

1. 异常向量表概述 从上面的地址映射来看&#xff0c;中断向量表的地址为0xD0037400&#xff0c;因此如果我们想在SRAM中&#xff0c;也就是BL1中处理异常的话&#xff0c; 就需要把我们的异常向量表拷贝到这个地址上。或者我们可以在链接脚本中直接指定代码的地址。 如果在主…

unity3d教程游戏包含的一切文件导入资源

http://www.58player.com/blog-2327-954.html 导入资源 将文件增加至工程文件夹的资源 (Assets) 文件夹后&#xff0c;Unity 将自动检测文件。将任何资源 (Assets) 放入资源文件夹后&#xff0c;资源 (Assets) 将显现在工程视图 (Project View) 中。 此工程视图 (Project Vie…

javascript 事件知识集锦

1.事件委托极其应用 转载的链接&#xff1a; http://www.webhek.com/event-delegate/#comments 2. 解析javascript事件机制 转载链接&#xff1a; http://www.nowamagic.net/javascript/js_EventAnalysis.php转载于:https://www.cnblogs.com/alicePanZ/p/4097017.html

ubuntu软件(查看文件差异)

你可以在ubuntu系统自带的软件---》ubuntu软件中心输入&#xff1a;meld diff 就可以安装。转载于:https://www.cnblogs.com/kobigood/p/4097411.html

关闭日志

/** Used by tools which include only core to disable log file creation. */ #ifndef ALLOW_LOG_FILE#define ALLOW_LOG_FILE 0 #endif 修改引擎源码的ALLOW_LOG_FILE为0

【Linux/Ubuntu学习6】unbuntu 下载android源码

在Windows下安装Cygwin&#xff0c;通过Cygwin也可在Windows里通过本文的下载步骤下载Android源码。以下为在Ubuntu下下载Google Android4.4源码的步骤&#xff1a; 1. 安装curl 与 git sudo apt-get install curl sudo apt-get install git-core 2 安装 Repo a) 建立Repo的安装…

瀑布流插件|jquery.masonry|使用demo

MaonsryInfinite-Scroll实现滚动式分页&#xff0c;网上有很多&#xff0c;这里只说&#xff1a; 瀑布流插件的一个基本使用&#xff0c;附上基本功能的demo <html> <head> <meta http-equiv"Content-Type" content"text/html; charsetutf-8&quo…

有关GNU GCC的基本内容整理

一、GCC简介 GCC&#xff08;GNU Compiler Collection&#xff0c;GNU编译器集合&#xff09;是一套由GNU工程开发的支持多种编程语言的编译器。GCC是自由软件发展过程中的著名例子&#xff0c;由自由软件基金会 以GPL协议发布。当年Richard Stallman 刚开始写作 GCC 的时候&am…