创建线程
ue的线程创建通过继承Runnable接口,调用FRunnableThrad::Create()来创建线程,没错,UE是有接口概念的
class FRunnable
Interface for "runnable" objects.
关于线程的启动与停止,有三个函数:bool Init() uint32 Run(), void Exit(),其中,只有Exit是运行在原有线程的,其余的都在新线程中运行
还有一个函数class FSingleThreadRunnable* GetSingleThreadInterface( )用于提供线程Tick的支持Gets single thread interface pointer used for ticking this runnable when multi-threading is disabled. * If the interface is not implemented, this runnable will not be ticked when FPlatformProcess::SupportsMultithreading() is false.
Code On Create Thread
//Crate Thread via FRunnable
TSharedPtr<FMETestRunnable> testRunnable=MakeShared<FMETestRunnable>();
auto name=TEXT("name");
auto runnableThreadPtr=FRunnableThread::Create(testRunnable.Get(),name);
if (testRunnable.IsValid())
{
runnableThreadPtr->WaitForCompletion();
}
std::future In UE
正如STL提供的异步框架一样,ue同样有std::future类似的异步函数
//like std::async ,execute a function asynchronously ,but do that on a new thread or via thread pool
//depends on the first parameter
auto result=Async(EAsyncExecution::Thread, []() {
for (int i=0;i<10;++i)
{
FPlatformProcess::Sleep(0.04);
UE_LOG(LogTemp,Warning,TEXT("Asyncing%d"),i);
}
return 0;
});
auto r=result.Get();
线程池:TaskGraph的使用
可以通过Asynctask()将任务推进线程池中运行,也可以在Async()中指明是否使用TaskGraph.
//Convenience function for executing code asynchronously on the Task Graph without create a new thread
AsyncTask(ENamedThreads::AnyThread,[&](){
FPlatformProcess::Sleep(0.04);
UE_LOG(LogTemp,Warning,TEXT("Asynxing"));
});
在实际引用中,往往多个并行任务存在前后依赖关系,形成类似AOV网的关系图,UE提供了相匹配的函数
//use thread pool to run a chain of task like AOV net
FGraphEventRef testEvent=FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
for (int i=0;i<10;++i)
{
FPlatformProcess::Sleep(0.04);
UE_LOG(LogTemp,Warning,TEXT("GraphTask%d"),i);
}
});
check(!testEvent->IsComplete());
testEvent->Wait();
FGraphEventArray testGraphEventArray;
testGraphEventArray.Add(testEvent);
for (int i=0;i<3;++i)
{
testGraphEventArray.Add(FFunctionGraphTask::CreateAndDispatchWhenReady([&]()
{
UE_LOG(LogTemp,Warning,TEXT("event arraying"));
}));
}
FGraphEventRef testEventB=FFunctionGraphTask::CreateAndDispatchWhenReady([]()
{
UE_LOG(LogTemp,Warning,TEXT("prerequisites Finish"));
},TStatId{},&testGraphEventArray);
testGraphEventArray.Add(testEventB);
FTaskGraphInterface::Get().WaitUntilTasksComplete(MoveTemp(testGraphEventArray));
当需要多次运行任务时,可以使用ParallelFor
//a convenience way to run multiple task in thread pool
auto now=FDateTime::Now().GetTicks();
ParallelFor(10,[](int32 Index)
{
UE_LOG(LogTemp,Warning,TEXT("index:%d"),Index);
FPlatformProcess::Sleep(0.04);
},EParallelForFlags::BackgroundPriority|EParallelForFlags::Unbalanced);
UE_LOG(LogTemp,Warning,TEXT("Coses:%lld"),FDateTime::Now().GetTicks()-now);
if (testRunnable.IsValid())
{
runnableThreadPtr->WaitForCompletion();
}
线程安全
使用线程锁
FCriticalSection CriticalSection;
{
FScopeLock lock(&CriticalSection);
}
类似与STL中线程锁提供的功能
使用原子操作
频繁的,不恰当的使用线程锁会导致严重的性能下降,解决方案之一是使用原子操作避免线程竞争导致问题UE提供了原子类比如
FThreadSafeBool bAtomic;
bAtomic =false;
在源码中,这些类被Deprecated,建议使用std::atomic<>来实现原子操作。实际上,UE的原子类也是通过调用操作系统底层API来实现原子操作
可能由于历史原因,UE当时没有选择是同STL提供的原子库
uint32 FMETestRunnable::Run()
{
bAtomic =false;
FScopeLock lock(&CriticalSection);
auto CurrentID=FPlatformTLS::GetCurrentThreadId();
auto CurrentThread=FThreadManager::Get().GetThreadName(CurrentID);
FString str=FString::Printf(TEXT("--CurrentID:%d,--CurrentThreadname:%s"),CurrentID,*CurrentThread);
UE_LOG(LogTemp,Warning,TEXT("%s"),*str);
int count=10;
while (count--)
{
FPlatformProcess::Sleep(0.05);
UE_LOG(LogTemp,Warning,TEXT("Running"));
}
return 1;
}
评论区