创建线程

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;
}