23 UE5多线程 Runnable GraphTask(STL

2024-04-17  本文已影响0人  游戏开发程序员

线程

// 打印3个输入的数字
int ThreadFuncPrint(int8 id)
{
    for (int i = 0; i < 3; i++)
    {
        UE_LOG(TestLog, Warning, TEXT("dosomething: %d"), id);
    }

    return 1;
}

    // STL thread
    if (0)
    {
        // 最简单的线程
        thread t([]()
            {
                UE_LOG(TestLog, Warning, TEXT("thread short"));
            }
        );

        // 方法1 等待线程执行完成,阻塞调用该方法的线程
        t.join();

        // 2 将线程分离,使得线程的执行与主线程分离,主线程不再等待子线程的结束。
        //  如果主线程提前结束,可能会导致子线程无法完整执行。
        //t.detach();

        // thread_local
        std::thread t1(ThreadFuncPrint, 1);
        std::thread t2(ThreadFuncPrint, 2);
        t1.join();
        t2.join();
    }

UE FRunnable类

FRunnableThread类

    // UE RunnableThread
    if (0)
    {
        // 创建FRunnable对象
        FMyRunnable* MyRunnable1 = new FMyRunnable(1);
        FMyRunnable* MyRunnable2 = new FMyRunnable(2);

        // 创建线程对象,并关联FRunnable对象
        FRunnableThread* MyThread1 = FRunnableThread::Create(MyRunnable1, TEXT("MyThread1"));
        FRunnableThread* MyThread2 = FRunnableThread::Create(MyRunnable2, TEXT("MyThread2"));

        MyThread1->WaitForCompletion();
        MyThread2->WaitForCompletion();

        delete(MyThread1);
        delete(MyRunnable1);

        delete(MyThread2);
        delete(MyRunnable2);
    }

// 自定义的实现了FRunnable接口的类
class FMyRunnable : public FRunnable
{
public:
    FMyRunnable(int8 _id) : ID(_id) {}
    ~FMyRunnable() 
    {
        UE_LOG(LogTemp, Warning, TEXT("~FMyRunnable : %d"), ID);
    }

    virtual bool Init() override
    {
        UE_LOG(LogTemp, Warning, TEXT("FMyRunnable Init: %d"), ID);
        return true;
    }

    virtual uint32 Run() override
    {
        for (int i = 0; i < 3; i++)
        {
            UE_LOG(LogTemp, Warning, TEXT("FMyRunnable Run: %d"), ID);

            FPlatformProcess::Sleep(1.0f);
        }

        return 0;
    }

    virtual void Stop() override
    {
        UE_LOG(LogTemp, Warning, TEXT("FMyRunnable Stop: %d"), ID);
    }

    virtual void Exit() override
    {
        UE_LOG(LogTemp, Warning, TEXT("FMyRunnable Exit: %d"), ID);
    }

private:
    int8 ID;
};

FThreadManager类

FScopeLock类

FCriticalSection AsyncTaskLock;

// 定义一个异步任务
void MyAsyncTask()
{
    // 使用lock 确保每个线程函数1~5打印完毕, 不插队
    FScopeLock Lock(&AsyncTaskLock);

    // 模拟耗时操作,将数组中的每个元素都乘以2
    for (int32 Number : {1,2,3,4,5})
    {
        UE_LOG(TestLog, Warning, TEXT("MyAsyncTask print : %d fromTID: %d"), 
            Number, FPlatformTLS::GetCurrentThreadId());
        FPlatformProcess::Sleep(0.5f); // 模拟耗时操作
    }

    // 异步任务完成后,可以进行一些回调操作
    UE_LOG(LogTemp, Warning, TEXT("MyAsyncTask completed!"));
}

TFuture类 类似STL

    // async and future 方便的使用异步方法
    if (0)
    {
        // 异步启动一个分离线程在后台,不明确调用时间
        future<int> result1 = async(ThreadFuncPrint, 1);

        // 明确以异步方式启动目标函数,如果无法启动.抛出异常.
        future<int> result2 = async(launch::async, ThreadFuncPrint, 2);

        // 强制延缓调用,必须等到get启动
        future<int> result3 = async(launch::deferred, ThreadFuncPrint, 3);

        result1.wait(); // wait强制执行后台异步线程
        result3.get(); // get强制执行并且拿到返回值

        // get在单线程上也可以保证.get只能调用一次.
        int result = result1.get() + ThreadFuncPrint(4);

        // 检查有效性  get之后就无效了
        check(!result1.valid())
    }
    // UE Async
    if (0)
    {
        // 调用Async函数来执行异步任务
        TFuture<int> Result = Async(EAsyncExecution::Thread, []() { return 123; });
        UE_LOG(TestLog, Warning, TEXT("Async Result : %d"), Result.Get());
        check(Result.IsValid());

        TFuture<void> Result2 = Async(EAsyncExecution::Thread, MyAsyncTask);
        TFuture<void> Result3 = Async(EAsyncExecution::Thread, MyAsyncTask);
        TFuture<void> Result4 = Async(EAsyncExecution::Thread, MyAsyncTask);
        Result2.Get(); // 等待执行完毕
        UE_LOG(TestLog, Warning, TEXT("Async call MyAsyncTask"));
        check(Result2.IsValid());
    }

TPromise类

    // UE TPromise
    if (0)
    {
        // 承诺给一个bool结果,附加回调
        TPromise<bool> Promise([]()
            {
                UE_LOG(LogTemp, Display, TEXT("the promise is set "));
            });

        // 返回一个尚未兑现的未来
        TFuture<bool> Future = Promise.GetFuture();

        // AnyThread中执行
        FFunctionGraphTask::CreateAndDispatchWhenReady([&Promise]()
            {
                // 模拟执行一段任务
                FPlatformProcess::Sleep(3);
                UE_LOG(LogTemp, Display, TEXT("do the promise"));
                // 设置结果
                Promise.SetValue(true);
                UE_LOG(LogTemp, Display, TEXT("Promise.SetValue(true)"));
            });


        UE_LOG(LogTemp, Display, TEXT("waiting for the promise..."));

        // 等待实现承诺
        //  - Wait - 等结果出来
        //  - WaitFor - 等结果出来 || 在一段时间内
        //  - WaitUntil - 等结果出来 || 到某年某月某一天

        Future.Wait();
        //Future.WaitFor(FTimespan::FromSeconds(5));

        // 注意: 不保证Promise.SetValue(true)之后的代码执行
        if (Future.IsReady())
        {
            UE_LOG(LogTemp, Display, TEXT("promise future is %d"), Future.Get());
        }
        else // 结果未设置
        {
            UE_LOG(LogTemp, Display, TEXT("promise Future is not ready"));
        }
    }


TGraphTask类

    // UE TaskGraph
    if (1)
    {
        // 连续函数调用 延迟构造设计
        // 1 首先创建一个任务实例 CreateTask()
        // 2 对任务实例调用 ConstructAndDispatchWhenReady
        FGraphEventRef evnetRef1 = TGraphTask<FMyTask>::CreateTask().ConstructAndDispatchWhenReady(1, 5);

        // 前置任务集合
        FGraphEventArray RootTasks = { evnetRef1 };

        // 无牵无挂
        // FGraphEventRef evnetRef2 = TGraphTask<FMyTask>::CreateTask().ConstructAndDispatchWhenReady(6, 10);

        // 等待RootTasks里面的任务完成后再执行
        FGraphEventRef evnetRef2 = TGraphTask<FMyTask>::CreateTask(&RootTasks, ENamedThreads::AnyThread).ConstructAndDispatchWhenReady(6, 10);

        //  等待任务执行完成
        evnetRef1->Wait();
        //  等待任务执行完成
        evnetRef2->Wait();
    }
class FMyTask : public FNonAbandonableTask
{
public:
    FMyTask(int32 InStartValue, int32 InEndValue)
        : StartValue(InStartValue), EndValue(InEndValue){ }

    // 当前Task的记录类型
    static TStatId GetStatId()
    {
        RETURN_QUICK_DECLARE_CYCLE_STAT(FMyTask, STATGROUP_TaskGraphTasks);
    }

    // 定义任务在哪个线程上执行,这里使用线程池
    static ENamedThreads::Type GetDesiredThread()
    {
        return ENamedThreads::AnyThread;
    }

    // 是否被其他任务依赖 TrackSubsequents:依赖,FireAndForget:不依赖
    static ESubsequentsMode::Type GetSubsequentsMode()
    {
        return ESubsequentsMode::TrackSubsequents;
    }

    // 任务的执行函数
    void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent)
    {
        for (int32 i = StartValue; i <= EndValue; i++)
        {
            UE_LOG(LogTemp, Warning, TEXT("FMyTask DoTask value: %d"), i);

            FPlatformProcess::Sleep(1);
        }
    }

private:
    int32 StartValue;
    int32 EndValue;
};

UE 重要线程

总结

上一篇 下一篇

猜你喜欢

热点阅读