1. 程式人生 > >UE4 多線程(一)

UE4 多線程(一)

一個 控制 htm 時間 arr pau sleep 多線程 繼續

  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 多線程(一)