# 多线程

# 多线程基础

# 应用场景

  • 向服务器发送请求,或热加载。
  • 加载关卡。

# 基本操作

下面创建一个线程并进行切换:

  1. 创建一个接口TaskInterface:

    //=========================TaskInterface.h==============================
    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "UObject/Interface.h"
    #include "TaskInterface.generated.h"
    
    // This class does not need to be modified.
    UINTERFACE(MinimalAPI)
    class UTaskInterface : public UInterface
    {
        GENERATED_BODY()
    };
    
    /**
     * 
     */
    class PYTHONTEST_API ITaskInterface
    {
        GENERATED_BODY()
    
        // Add interface functions to this class. This is the class that will be inherited to implement this interface.
    public:
        virtual void DoWork(){};
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
  2. 创建一个线程类ThreadTask:

    //===============================ThreadTask.h====================================
    #pragma once
    #include "CoreMinimal.h"
    #include "TaskInterface.h"
    
    /**
     * 
     */
    DECLARE_DELEGATE(FThreadTask)
    
    class PYTHONTEST_API ThreadTask:public FRunnable
    {
    public:
        ThreadTask();
        ~ThreadTask();
    
        FThreadTask ThreadTaskDelegate;
        
        virtual uint32 Run() override;
        void CreateThread(ITaskInterface* TaskInterfaceTemp);
    
    private:
        ITaskInterface* TaskInterface;
        FRunnableThread *TaskTread;
    };
    
    //===============================ThreadTask.cpp====================================
    
    #include "ThreadTask.h"
    
    ThreadTask::ThreadTask()
    {
        TaskInterface = nullptr;
    }
    
    ThreadTask::~ThreadTask()
    {
    }
    
    uint32 ThreadTask::Run()
    {
        UE_LOG(LogTemp,Warning,TEXT("run thread!"));
        if(TaskInterface)
        {
            TaskInterface->DoWork();
            //切换线程
            FGraphEventRef MyTask = FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
            {
                ThreadTaskDelegate.ExecuteIfBound();
            },TStatId(),NULL,ENamedThreads::GameThread);
            FTaskGraphInterface::Get().WaitUntilTaskCompletes(MyTask);
        }
        return 0;
    }
    
    void ThreadTask::CreateThread(ITaskInterface* TaskInterfaceTemp)
    {
        this->TaskInterface=TaskInterfaceTemp;
        TaskTread = FRunnableThread::Create(this,TEXT("TestThread"),0,TPri_Normal);
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
  3. 下面来测试一下它们:

    //===========================RunThreadTaskEnv.h==============================
    ITaskInterface * interface;
    ThreadTask* NewTask;
    void PrintF();
    
    //===========================RunThreadTaskEnv.cpp==============================
    //执行主线程内容
    void ARunThreadTaskEnv::PrintF()
    {
        UE_LOG(LogTemp,Warning,TEXT("printf function! run in the main thread"));
    }
    
    // Called when the game starts or when spawned
    void ARunThreadTaskEnv::BeginPlay()
    {
        Super::BeginPlay();
        interface = new ITaskInterface();
        NewTask = new ThreadTask();
        NewTask->ThreadTaskDelegate.BindUObject(this,&ARunThreadTaskEnv::PrintF);
        NewTask->CreateThread(interface);
    }
    
    void ARunThreadTaskEnv::EndPlay(const EEndPlayReason::Type EndPlayReason)
    {
        delete NewTask;
        interface = nullptr;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27

# GraphTask

按照顺序执行多个线程:

  1. 创建TaskGraph.h文件:

    class FTaskGraph
    {
    public:
    
        FTaskGraph(float _f)
            :m_f(_f)
        {
    
        }
    
        static ESubsequentsMode::Type GetSubsequentsMode()
        {
            return ESubsequentsMode::TrackSubsequents;
        }
    
        FORCEINLINE TStatId GetStatId()
        {
            RETURN_QUICK_DECLARE_CYCLE_STAT(FTaskGraph, STATGROUP_TaskGraphTasks);
        }
    
        void DoTask(ENamedThreads::Type CurrentThread, FGraphEventRef Subsequents)
        {
            UE_LOG(LogTemp, Warning, TEXT("Hello World %f"), m_f);
        }
    
        static ENamedThreads::Type GetDesiredThread()
        {
            return ENamedThreads::AnyThread;
        }
    
    protected:
    private:
        float m_f;
    };
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
  2. 按先后顺序创建多线程:

    //延时构造
    TGraphTask<FTaskGraph>::CreateTask(NULL, ENamedThreads::GameThread).ConstructAndDispatchWhenReady(4.5f);
    
    1
    2

# AsyncTask

创建异步线程:

  1. 创建TaskAsyncTask.h文件:

    class TaskAsyncTask : public FNonAbandonableTask
    {
        friend class FAsyncTask<TaskAsyncTask>;
    
        int32 InstanceInt;
    
        TaskAsyncTask( int32 _InstanceInt)
            :InstanceInt(_InstanceInt)
        {
    
        }
    
        void DoWork()
        {
            UE_LOG(LogTemp, Warning, TEXT("DoWork %d"), InstanceInt);
        }
    
        FORCEINLINE TStatId GetStatId() const
        {
            RETURN_QUICK_DECLARE_CYCLE_STAT(TaskAsyncTask, STATGROUP_ThreadPoolAsyncTasks);
        }
    };
    
    void m_Main()
    {
        //可使用一个现有的类
        AsyncTask(ENamedThreads::GameThread, [&]() {
            UE_LOG(LogTemp, Warning, TEXT("DoWork"));
        });
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
  2. 使用上面类创建异步线程:

    void ARunThreadTaskEnv::BeginPlay()
    {
        FAsyncTask<TaskAsyncTask> *MyTask = new FAsyncTask<TaskAsyncTask>(3);
        MyTask->StartBackgroundTask();
        //MyTask->StartSynchronousTask();//执行线程为游戏线程,同步执行。
        if(MyTask->IsDone())
        {
            UE_LOG(LogTemp, Warning, TEXT("MyTask->IsDone() is finished!"));
        }
        MyTask->EnsureCompletion();
        delete MyTask;
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12

# 总结

  1. Runnable:用于复杂计算。
  2. GraphTask:可规定多线程任务的执行顺序。
  3. AsyncTask:从线程池中调用,使用空闲线程。

注意下面操作只能在主线程中使用:

  1. SpawnActor
  2. NewObject
  3. Destroy UObject/Actor
  4. DrawDebugLine