• 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使用前置声明。

      

  • 相关阅读:
    记录一下
    Fiddler对谷歌浏览器抓包
    Linux环境部署基本步骤
    JS----this && JS继承
    节流与防抖
    JS---call apply bind的区别&&JS---argument
    浏览器输入url之后到最后网页渲染出来经历了什么
    Bom中的方法
    JS----new和object.create的区别
    有关排序
  • 原文地址:https://www.cnblogs.com/litmin/p/8337158.html
Copyright © 2020-2023  润新知