UE4 多線程(一)
UE4中使用多線程的有兩種方式,一種方式就是使用FRunnable和FRunnableThread,另一種方式是Task Graph System。Task Graph System有時會占用遊戲線程的時間,適合用在簡單的計算或者需要開多個線程的情況。FRunnable適用於復雜運算。但是當創建太多線程後,有可能達到CPU的上限,這些線程就會為了搶占CPU的時間而彼此阻礙。UE4提供了FQueuedThreadPool來限制最大的線程數量。
這種方式需要定義一個類繼承自FRunnable,並且實現Init,Run,Stop,Exit。還需要一個FRunnableThread對象來創建和銷毀線程。
具體代碼:
#pragma once #include "CoreMinimal.h" #include "Runnable.h" #include "RunnableThread.h" class TESTTARRAY_API AudioRecordThread : public FRunnable { public: AudioRecordThread(FString threadName); ~AudioRecordThread(); //暫停線程 void PauseThread(); //繼續線程 void ContinueThread();//停止線程 void StopThread(); bool IsThreadPaused(); bool IsThreadKilled(); private: FRunnableThread* Thread; FThreadSafeCounter StopTaskCounter; FCriticalSection m_mutex; public: //override Frunnable Function virtual bool Init() override; virtual uint32 Run() override;virtual void Stop() override; virtual void Exit() override; private: FThreadSafeBool m_Kill; FThreadSafeBool m_Pause; };
#include "AudioRecordThread.h" #include "Engine.h" AudioRecordThread::AudioRecordThread(FString threadName) : StopTaskCounter(0) { m_Kill = false; m_Pause = false; Thread = FRunnableThread::Create(this, *threadName, 0, TPri_BelowNormal); } AudioRecordThread::~AudioRecordThread() { if (Thread) { delete Thread; Thread = nullptr; } } void AudioRecordThread::PauseThread() { m_Pause = true; } void AudioRecordThread::ContinueThread() { m_Pause = false; } void AudioRecordThread::StopThread() { Stop(); if (Thread) { Thread->WaitForCompletion(); } } //需要註意的是在其他線程不能對UObject進行操作,不能使用TimerManager,不能使用DrawDebugLine。 bool AudioRecordThread::IsThreadPaused() { return (bool)m_Pause; } bool AudioRecordThread::IsThreadKilled() { return (bool)m_Kill; } bool AudioRecordThread::Init() { GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("AudioRecordThreadInit")); return true; } uint32 AudioRecordThread::Run() { //使用該函數Sleep FPlatformProcess::Sleep(0.03); while (StopTaskCounter.GetValue() == 0 && !m_Kill) { if (m_Pause) { GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("AudioRecordThreadPause")); if (m_Kill) { return 0; } } else { GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("AudioRecordThreadLoop")); m_mutex.Lock(); //需要同步處理的內容 m_mutex.Unlock(); FPlatformProcess::Sleep(0.01); } } return 0; } void AudioRecordThread::Stop() { StopTaskCounter.Increment(); m_Kill = true; m_Pause = false; GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("AudioRecordThreadStop")); } void AudioRecordThread::Exit() { GEngine->AddOnScreenDebugMessage(-1, 10.0f, FColor::Yellow, TEXT("AudioRecordThreadExit")); }
創建線程:
m_AudioRecordThread = new AudioRecordThread("AudioRecordThread1");
, 構造函數中會用FRunnableThread::Create來創建線程。
停止線程:
if (m_AudioRecordThread) { m_AudioRecordThread->StopThread(); delete m_AudioRecordThread; m_AudioRecordThread = nullptr; }
在StopThread中會調用Stop來控制相關狀態量完成線程循環,Thread->WaitForCompletion(),會使調用StopThread的線程即主線程懸掛,當線程循環完成後繼續,繼續後刪除m_AudioRecordThread對象。需要註意的是這樣停止線程會調用兩次Stop函數,所以Stop中最好只執行控制線程循環停止的狀態量。
使用FPlatformProcess::Sleep(0.03)來等待,避免線程占用過多的資源。
線程鎖:
使用m_mutex.Lock();和m_mutex.Unlock();包裹住需要同步的代碼即可。
需要註意的是在其他線程不能對UObject進行操作,不能使用TimerManager,不能使用DrawDebugLine。要在遊戲線程執行代碼可以這樣用:
#include "Async.h" ... AsyncTask(ENamedThreads::GameThread, []() { // code to execute on game thread here });
另外我這裏使用多線程是做錄音相關功能,用到了AudioClient.h這個頭文件,因為這個文件用到了windows的東西,所以用在UE4裏會報錯,所以需要把這個頭文件放到CPP裏包含,用到的struct使用前置聲明。
UE4 多線程(一)