无用的前言
FRunnable
可以在主线程之外的线程中执行代码,当需要并行做某些事的时候会有用。
虽然在之前的《UE4使用FScopeLock做线程锁》中已顺便创建一个简单的FRunnable
的代码,但是不够简单。我想再整理下,得到一个最简单的代码,方便之后用到时直接复制这里。
目标
整理出“建立一个最简单的FRunnable所需的代码”。
1. 定义新的Runnable
首先,需要继承FRunnable
创建自己的子类:
TestRunnable.h:
#pragma once
#include "HAL/Runnable.h"
#include "HAL/RunnableThread.h"
class FTestRunnable : public FRunnable
{
public:
// FRunnable methods.
virtual uint32 Run() override;
virtual void Stop() override;
};
想要在这个线程中执行的代码放在Run
中。而当在外部终止这个Runnable时,Stop
会被调用。
我这里用于测试的 TestRunnable.cpp 如下:
#include"TestRunnable.h"
#include"CoreMinimal.h"
#include"HAL/PlatformProcess.h"
DEFINE_LOG_CATEGORY_STATIC(LogYaksueTest, Log, All);
uint32 FTestRunnable::Run()
{
//在此处写想要执行的代码:
{
//作为测试,每1秒打印一次当前时间,执行10次
for (int i = 0; i < 10; i++)
{
FPlatformProcess::Sleep(1.0f);
UE_LOG(LogYaksueTest, Warning, TEXT("%s"), *FDateTime::Now().ToString());
}
}
return 0;
}
void FTestRunnable::Stop()
{
UE_LOG(LogYaksueTest, Warning, TEXT("Stop!"));
}
2. 创建Runnable和RunnableThread
然后,不仅需要创建FRunnable
,还需要创建一个FRunnableThread
。
这里我为了方便,直接声明两个 static 指针用于存放他们:
static FRunnableThread* TestRunnableThread = nullptr;
static FTestRunnable* TestRunnable = nullptr;
启动时,new
出一个Runnable,然后用FRunnableThread::Create
创建出RunnableThread:
TestRunnable = new FTestRunnable();
TestRunnableThread = FRunnableThread::Create(TestRunnable, TEXT("TestRunnable"), 0, TPri_Normal);
3. 终止时调用Kill
想要终止时,调用Kill
:
TestRunnableThread->Kill();
delete TestRunnableThread;
TestRunnableThread = nullptr;
效果
启动后,Runnable就开始执行了,我这里是测试每秒打印1次log。由于不是在主线程中执行的,所以就算用了 FPlatformProcess::Sleep 编辑器也没有卡死。并且,主线程中的操作也不会影响Runnable的执行。
另外要注意的是:我其实在3秒多的时候就调用了Kill
,可以看到FTestRunnable::Stop()
也确实被调用了。但是Run
中的代码还在继续执行(并且此时编辑器卡死)。因此,如果想要终止得更及时,应该需要围绕Stop
做些逻辑(比如在Stop
中将 bStopping 设置为true,而在Run
中检查到 bStopping 为true就立马停止执行后续的代码)
总结
在UE中建立一个最简单的FRunnable所需的代码为:
- 定义新的
FRunnable
子类,在Run
中放想要这个线程执行的代码,在Stop
中放外部终止它时应该被调用的代码。 - 启动时,
new
出一个 Runnable,然后用FRunnableThread::Create
创建出 RunnableThread。 - 终止时,调用 RunnableThread 的
Kill
: