Unreal – Multithreading

Original text portal

FRunnable & FRunnableThread

FRunnable provides the most primitive thread support, similar to std::Thread. Although it is not as elegant, it has slightly more functions than std::Thread.

  • FRunnable—carrying business logic
    • +[f]Init : Initialization, can fail
    • +[f]Run : The thread runs the function and returns the exit code
    • +[f] Stop : Terminate this thread early
    • +[f]Exit : Clean up before exiting
    • +[f] GetSingleThreadInterface : FeedBack policy when the platform does not support multi-threading

  • FRunnableThread—thread entity
    • -[sm] RunnableTlsSlot : TLS Slot, used to implement dynamic Thread Local
    • ——————————-split——————————-
    • -[m]ThreadName : Thread name
    • -[m] Runnalbe : Runnable object for specific execution
    • -[m] ThreadInitSyncEvent : The main thread waits for events to ensure that Init is completed
    • -[m] TlsInstances : Thread local variable
    • -[m] ThreadPriority : Thread priority
    • -[m] ThreadID : Thread ID
    • ——————————-split——————————-
    • +[f] GetTlsSlot : Get TLS Slot
    • +[f] SetThreadPriority : Set thread priority
    • +[f]Suspend : Suspend or resume a thread
    • +[f] Kill : kill thread, optional wait
    • +[f] WaitForCompletion : Wait for thread to complete
    • ——————————-split——————————-
    • +[sf] Create : Create a thread to run the Runnable

  • FThreadManager—thread manager
    • -[m] Threads : all threads
    • +[f] AddThread : Add the thread to management and call it when the thread is created.
    • +[f] RemoveThread : remove thread
    • +[f] Tick : For Fake thread, it will be executed in Tick
    • +[f] GetThreadName : Get the thread name through Id
    • +[f] IsInitialized : Whether initialization is completed
    • +[f] ForEachThread : Traverse threads
    • +[sf] Get : get singleton

  • usage
    FRunnable * Runnable = new MyRunnable();
    FRunnableThread* RunnableThread = FRunnableThread::Create(Runnable, TEXT("MyThread"));

AsynTask

AsynTask implements pooled tasks. Dependencies are not supported between tasks, but they can be recycled. Tasks are executed by worker threads.

  • IQueuedWork—the upper-level abstraction of Work
    • +[f] DoThreadedWork : Execute Work
    • +[f] Abandon : Give up executing Work and only call it from the thread pool

  • FQueuedThread—fake Thread, real Runnable
    • -[m] DoWorkEvent : Used to wake up Worker work
    • -[m] TimeToDie : Used to tell the Worker to exit the thread
    • -[m] QueuedWork : Current Work
    • -[m] OwningThreadPool : Dependent ThreadPool
    • -[m] Thread : the thread to which it is attached
    • ——————————-split——————————-
    • -[f]Run : The implemented Run interface function is internally a loop with TimeToDie as the condition. It waits when there is no Work. ThreadPool fills in the Work and environment. When the work is completed, it will apply to the thread pool or return itself to the thread pool.
    • +[f]Create : Create a real thread and attach it to it, then add it to the thread pool
    • +[f] KillThread : Kill the thread and block waiting before actually killing it.
    • +[f] DoWork : Execute Work, nothing fancy

  • FQueuedThreadPool—thread pool
    • +[f]Create : Create a specified number of threads
    • +[f] Destroy : Destroy the thread pool
    • +[f] AddQueuedWork : Add Work
    • +[f] RetractQueuedWork : Try to recycle Work
    • +[f] GetNumThreads : Get the number of worker threads
    • ——————————-split——————————-
    • -[sf] Allocate : Create a thread pool (FQueuedThreadPoolBase)
    • -[sm] OverrideStackSize : Minimum stack size when creating a thread

  • FAsyncTask—convenient shell template
    • -[m]Task : Template Task
    • -[m] WorkNotFinishedCounter : Used to mark whether the Task is completed
    • -[m]DoneEvent : Event used to wait for Task completion
    • -[m] QueuedPool : The thread pool to which the Task is attached
    • ——————————-split——————————-
    • -[f] DestroyEvent : Internal use, recycling Event
    • -[f] Start : Internal use, start executing Task
    • -[f] DoWork : Internally used to execute the Task. After execution, the Finished count will be decremented by one.
    • -[f] FinishThreadedWork : Internal use, triggers DoneEvent
    • -[f] DoThreadWork : Internal use, calling the above two functions in sequence
    • -[f] Abandon : Internal use, discard the task
    • -[f] CheckIdle : Internal use to detect whether it is an independent task
    • -[f] SyncCompletion : Internal use, waiting for task completion
    • -[f]Init : Initialization, only blanking
    • +[f] GetTask : Get Task reference
    • +[f] StartSynchronousTask : Start blocking task
    • +[f] StartBackgroundTask : Start an asynchronous task
    • +[f] EnsureCompletion : Ensure that Task execution is completed, optionally blocking execution immediately
    • +[f]Cancel :Recover Task
    • +[f] WaitCompletionWithTimeout : Waiting for completion with timeout
    • +[f]IsDone : Whether the task is completed, the Event will be automatically reset for reuse.
    • +[f] IsWorkDone : Whether the task is completed, but no status will be reset
    • +[f]IsIdle : Whether the task has not been started or not completed
    • ——————————-split——————————-
    • Functions that TTask needs to implement
      • CanAbandon : whether it can be discarded
      • Abandonment : discard task
      • DoWork : executor
      • GetStatId : Get the StatId of the task

  • FNonAbandonableTask—Task that cannot be discarded
    • AchievedCanAbandonAbandonmentfunction, tasks that inherit it only need to implement DoWork and GetStatId

  • usage
    
    class MyTask : public FNonAbandonableTask
    {
    public:
    void DoWork() { xxxx }
    FORCEINLINE TStatId GetStatId() const
    {
        RETURN_QUICK_DECLARE_CYCLE_STAT(MyTask, STATGROUP_ThreadPoolAsyncTasks);
    }
    }
    void Example()
    {
    FAsyncTask<MyTask> Task;
    // start up
    Task->StartBackgroundTask();
    // xxxx Other operations 
    // wait complet 
    Task->EnsureCompletion();
    }


### TaskGraph
> TaskGraph

- **FSingleThreadRunnable----Fake线程**
    - **+[f] Tick** : 在单线程情况下调用Tick来模拟多线程

--------------------------------------------------------------------------

- **FWorkerThread----帮助结构体**
    - **+[m] TaskGraphWorker** : Task工作线程
    - **+[m] RunnableThread** : 实体线程
    - **+[m] bAttached** : Task工作线程是否Attach到实体线程上
- **FTaskThreadBase----Task工作线程基类, Runnable**
    - **-[m] ThreadId** : ENamedThreads的枚举用来标识当前线程
    - **-[m] PerThreadIDTLSSlot** : TLS Slot
    - **-[m] IsStalled** : 用于标记是否失速 ?
    - **-[m] NewTasks** : 当前线程得到的新任务
    - **-[m] OwnerWorker** : 持有本工作线程的Worker
    - -------------------------------split-------------------------------
    - **+[f] SetUp** : 设置一些基础信息
    - **+[f] InitializeForCurrentThread** : 仅调用一次,设置TLS变量
    - **+[f] GetThreadId** : 得到ThreadID
    - **+[f] ProcessTasksUntilQuit** : 一直执行Task直到退出
    - **+[f] ProcessTasksUntilIdle** : 一直执行Task直到Idle被调用
    - **+[f] EnqueueFromThisThread** : 入队Task,假定是从当前线程调用
    - **+[f] EnqueueFromOtherThread** : 入队Task,假定从其它线程调用
    - **+[f] RequestQuit** : 请求结束线程 
    - **+[f] WakeUp** : 唤醒线程
    - **+[f] IsProcessingTasks** : 是否在处理任务
- **FNamedTaskThread----命名线程**
    - 从自己的工作队列中提取Task
- **FTaskThreadAnyThread----工作线程**
    - 从整体的Task池中提取Task

--------------------------------------------------------------------------

- **FTaskGraphInterface** : TaskGraph系统对外接口
- **FTaskGraphImplementation** : TaskGraphInterface的实现体
- **FBaseGraphTask** : 所有Task的基类
- **FGraphEvent** : Task之间的通知机制,用于形成依赖网络
- **TGraphTask** : 方便使用的模板

### Reference
- [《Exploring in UE4》多线程机制详解](https://zhuanlan.zhihu.com/p/38881269)
- [UE4 C++基础教程 - 多线程](https://zhuanlan.zhihu.com/p/133921916)

Post Reply