如何在UE4中创建线程

FRunnableFRunnableThread方法对于大多数问题来说无疑是一个可行的解决方案。 但是,在创建许多任务时,您可能会达到CPU可以处理的并发上限,此时并发线程实际上会在争用CPU时间时相互阻碍。 然后可能值得查看FQueuedThreadPool以限制任务可用的线程数。

虚幻引擎4还提供了全局GThreadPool ,但是此线程池仅设置为单个线程(UE4.14.3)。 它似乎只是为了在另一个核心上运行并发任务。

下面的代码是什么

  • 创建一个线程来计算前50,000个素数
  • 将增量完成数据发送回游戏线程
  • 具有静态访问器,用于启动,关闭和查找线程是否已完成。

静态函数

您将在下面的代码中注意到我使用静态函数轻松启动新线程,如果我需要匆忙关闭线程(例如播放器退出),我也可以使用GameThread中的静态Shutdown()函数游戏)

性能

我首先使用任务图系统计算前50,000个素数,并创建50,000个任务(每个素数找1个)。

在本教程中看到的代码中,我创建了一个专用线程来计算前50,000个素数!

性能优势是非凡的!

我的fps在下面的代码中为这个线程的整个运行保持稳定90(我选择的最大fps)。

而对于任务图系统,当我接近50,000时,fps下降了最多40。

对于较大的任务,请务必尝试实际的多线程!

.H

//~~~~~ Multi Threading ~~~
class FPrimeNumberWorker : public FRunnable
{	
	/** Singleton instance, can access the thread any time via static accessor, if it is active! */
	static  FPrimeNumberWorker* Runnable;
	
	/** Thread to run the worker FRunnable on */
	FRunnableThread* Thread;
	
	/** The Data Ptr */
	TArray<uint32>* PrimeNumbers;
	
	/** The PC */
	AVictoryGamePlayerController* ThePC;
	
	/** Stop this thread? Uses Thread Safe Counter */
	FThreadSafeCounter StopTaskCounter;
	
	//The actual finding of prime numbers
	int32 FindNextPrimeNumber();
	
private:
	int32				PrimesFoundCount;
public:
	
	int32				TotalPrimesToFind;

	//Done?
	bool IsFinished() const
	{
		return PrimesFoundCount >= TotalPrimesToFind;
	}
	
	//~~~ Thread Core Functions ~~~
	
	//Constructor / Destructor
	FPrimeNumberWorker(TArray<uint32>& TheArray, const int32 IN_PrimesToFindPerTick, AVictoryGamePlayerController* IN_PC);
	virtual ~FPrimeNumberWorker();

	// Begin FRunnable interface.
	virtual bool Init();
	virtual uint32 Run();
	virtual void Stop();
	// End FRunnable interface
	
	/** Makes sure this thread has stopped properly */
	void EnsureCompletion();
	
	
	
	//~~~ Starting and Stopping Thread ~~~
	
	
	
	/* 
		Start the thread and the worker from static (easy access)! 
		This code ensures only 1 Prime Number thread will be able to run at a time. 
		This function returns a handle to the newly started instance.
	*/
	static FPrimeNumberWorker* JoyInit(TArray<uint32>& TheArray, const int32 IN_TotalPrimesToFind, AVictoryGamePlayerController* IN_PC);

	/** Shuts down the thread. Static so it can easily be called from outside the thread context */
	static void Shutdown();

        static bool IsThreadFinished();

};

.CPP

//***********************************************************
//Thread Worker Starts as NULL, prior to being instanced
//		This line is essential! Compiler error without it
FPrimeNumberWorker* FPrimeNumberWorker::Runnable = NULL;
//***********************************************************

FPrimeNumberWorker::FPrimeNumberWorker(TArray<uint32>& TheArray, const int32 IN_TotalPrimesToFind, AVictoryGamePlayerController* IN_PC)
	: ThePC(IN_PC)
	, TotalPrimesToFind(IN_TotalPrimesToFind)
	, StopTaskCounter(0)
	, PrimesFoundCount(0)
{
	//Link to where data should be stored
	PrimeNumbers = &TheArray;
	
	Thread = FRunnableThread::Create(this, TEXT("FPrimeNumberWorker"), 0, TPri_BelowNormal); //windows default = 8mb for thread, could specify more
}

FPrimeNumberWorker::~FPrimeNumberWorker()
{
	delete Thread;
	Thread = NULL;
}

//Init
bool FPrimeNumberWorker::Init()
{
	//Init the Data 
	PrimeNumbers->Empty();
	PrimeNumbers->Add(2);
	PrimeNumbers->Add(3);
	
	if(ThePC) 
	{
		ThePC->ClientMessage("**********************************");
		ThePC->ClientMessage("Prime Number Thread Started!");
		ThePC->ClientMessage("**********************************");
	}
	return true;
}

//Run
uint32 FPrimeNumberWorker::Run()
{
	//Initial wait before starting
	FPlatformProcess::Sleep(0.03);
	
	//While not told to stop this thread 
	//		and not yet finished finding Prime Numbers
	while (StopTaskCounter.GetValue() == 0 && ! IsFinished())
	{
		PrimeNumbers->Add(FindNextPrimeNumber());
		PrimesFoundCount++;
		
		//***************************************
		//Show Incremental Results in Main Game Thread!
		
		//	Please note you should not create, destroy, or modify UObjects here.
		//	  Do those sort of things after all thread are completed.
		
		//	  All calcs for making stuff can be done in the threads
		//	     But the actual making/modifying of the UObjects should be done in main game thread.
		ThePC->ClientMessage(FString::FromInt(PrimeNumbers->Last()));
		//***************************************
		
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		//prevent thread from using too many resources
		//FPlatformProcess::Sleep(0.01);
		//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	}
	
	//Run FPrimeNumberWorker::Shutdown() from the timer in Game Thread that is watching
        //to see when FPrimeNumberWorker::IsThreadFinished()
	
	return 0;
}

//stop
void FPrimeNumberWorker::Stop()
{
	StopTaskCounter.Increment();
}

FPrimeNumberWorker* FPrimeNumberWorker::JoyInit(TArray<uint32>& TheArray, const int32 IN_TotalPrimesToFind, AVictoryGamePlayerController* IN_PC)
{
	//Create new instance of thread if it does not exist
	//		and the platform supports multi threading!
	if (!Runnable && FPlatformProcess::SupportsMultithreading())
	{
		Runnable = new FPrimeNumberWorker(TheArray,IN_TotalPrimesToFind,IN_PC);			
	}
	return Runnable;
}

void FPrimeNumberWorker::EnsureCompletion()
{
	Stop();
	Thread->WaitForCompletion();
}

void FPrimeNumberWorker::Shutdown()
{
	if (Runnable)
	{
		Runnable->EnsureCompletion();
		delete Runnable;
		Runnable = NULL;
	}
}

bool FPrimeNumberWorker::IsThreadFinished()
{
	if(Runnable) return Runnable->IsFinished();
	return true;
}
int32 FPrimeNumberWorker::FindNextPrimeNumber()
{
	//Last known prime number  + 1
	int32 TestPrime = PrimeNumbers->Last();
	
	bool NumIsPrime = false;
	while( ! NumIsPrime)
	{
		NumIsPrime = true;
		
		//Try Next Number
		TestPrime++;
		
		//Modulus from 2 to current number - 1 
		for(int32 b = 2; b < TestPrime; b++)
		{
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			//prevent thread from using too many resources
			//FPlatformProcess::Sleep(0.01);
			//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
			if(TestPrime % b == 0) 
			{
				NumIsPrime = false;
				break;
				//~~~
			}
		}
	}
	
	//Success!
	return TestPrime;
}

Starting the thread

//In the .h for the player controller
// this is the actual data
TArray<uint32> PrimeNumbers;

 

//player controller .cpp
//Multi-threading, returns handle that could be cached.
//		use static function FPrimeNumberWorker::Shutdown() if necessary
FPrimeNumberWorker::JoyInit(PrimeNumbers, 50000, this);

线程管理

您应该查看“FRunnable”的代码库,以查看多线程和锁定/解锁保护的扩展用途。

我用了一个简单的例子来帮助你入门,但是多线程时需要考虑很多:)

使用Sleep进行线程管理

你应该考虑使用

  FPlatformProcess :: Sleep ( 秒 );

防止1个线程占用太多系统资源:)

什么不该做

  • 不要尝试从其他线程修改,创建或删除UObject!

您可以准备所有数据/进行所有计算,但只有游戏线程应该实际产生/修改/删除UObjects / AActors。

  • 不要尝试在游戏线程之外使用TimerManager :)
  • 不要试图绘制调试行/点等,因为它可能会崩溃,即DrawDebugLine(等...)
  • 通知(自4.11起):

    如果要使用计时器,删除和修改变量,请使用它:

      #include “Async.h” 
     ...
     AsyncTask ( ENamedThreads :: GameThread , []() {
          //这里在游戏线程上执行的代码
      });
    

    GameThread中的计时器功能

    您可以在游戏线程中运行计时器功能,以定期检查您创建的其他线程收集的数据。

    如何支持单线程平台?

    如果您的代码绝对必须在HTML5等单线程环境中运行,那么请查看

    AsyncIOSystemBase.h

      struct CORE_API FAsyncIOSystemBase:public FIOSystem,FRunnable,FSingleThreadRunnable
    

    Runnable可以扩展SingleThreadRunnable,并为单线程情况下的FRunnable钩子返回自己:

/**
 * 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.
 *
 * @return Pointer to the single thread interface or nullptr if not implemented.
 */
virtual class FSingleThreadRunnable* GetSingleThreadInterface( )
{
	return nullptr;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 答1: 在UE4,可以通过使用多线程来运行插件,以提高程序的性能和并行处理能力。 首先,插件在UE4是以模块的形式存在的,可以独立于游戏项目而存在。为了实现多线程运行插件,可以在插件的模块编写异步任务,利用多线程执行这些任务。 在UE4,我们可以使用FRunnable接口来创建自定义线程。首先,需要创建一个继承自FRunnable的类,并实现必要的接口函数,如Run和Stop。在Run函数,编写插件需要执行的任务代码。 在插件的初始化阶段,可以通过调用FRunnableThread::Create函数来创建一个新的线程,并指定我们刚刚定义的FRunnable类对象作为参数。然后,可以开始执行插件任务。 需要注意的是,在多线程编程,需要合理地处理线程之间的数据共享和同步问题。可以使用一些线程同步的机制,如互斥锁、信号量等,来避免多个线程同时操作共享数据导致的冲突。 另外,UE4还提供了一些已经封装好的多线程工具类,如FGraphEvent和FQueuedThread等,可以帮助我们更方便地实现多线程任务的管理和同步。 总结来说,UE4可以通过使用FRunnable接口和多线程工具类来实现插件的多线程运行。合理地设计和管理多线程任务,可以提高程序的性能和并行处理能力,并且确保线程之间的数据共享和同步的正确性。 ### 答2: 在UE4,我们可以通过多线程来实现插件的运行。多线程是一种并发执行任务的方式,可以让程序同时执行多个任务,提高了程序的效率和性能。 在UE4,我们可以使用一些多线程的技术来实现插件的运行。比如,使用C++的std::thread来创建多个线程,并让每个线程在后台执行插件相关的任务。同时,我们还可以使用一些线程同步的机制,比如互斥锁、条件变量等来管理多个线程之间的资源访问和共享。 在插件的运行过程,可以将一些耗时的任务放在单独的线程执行,这样就不会阻塞主线程执行。比如,可以将插件的某个算法或者处理逻辑放在一个独立的线程运行,这样主线程可以继续执行其他的任务,提高了程序的响应速度和用户体验。 除了使用多线程,还可以使用UE4提供的任务图谱系统来实现插件的并行执行。任务图谱系统可以将任务划分成多个小任务,并按照依赖关系进行调度,从而充分利用计算资源并提高任务执行的效率。 总结来说,UE4可以通过多线程技术来实现插件的运行,可以提高程序的效率和性能,同时还可以使用任务图谱系统来实现任务的并行执行。这些技术可以充分发挥计算资源的利用率,提高插件的运行效果和用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值