El multithreading és una tècnica avançada de programació que permet executar múltiples fils d'execució de manera concurrent dins d'una aplicació. En el context d'Unreal Engine, el multithreading pot ser utilitzat per millorar el rendiment del joc, permetent que diferents tasques es processin simultàniament. Aquest mòdul t'introduirà als conceptes bàsics del multithreading en C++ i com implementar-lo en Unreal Engine.
Conceptes Clau
- Fil (Thread): Un fil és la unitat bàsica d'execució dins d'un procés. Cada fil té el seu propi conjunt de registres, pila i estat.
- Procés: Un procés és un programa en execució que pot contenir múltiples fils.
- Sincronització: La sincronització és el procés de coordinar l'execució de múltiples fils per evitar condicions de carrera i altres problemes de concurrència.
- Mutex: Un mutex (mutual exclusion) és un mecanisme de sincronització que assegura que només un fil pugui accedir a una secció crítica del codi a la vegada.
- Condició de Carrera: Una condició de carrera ocorre quan dos o més fils accedeixen i modifiquen dades compartides de manera concurrent, produint resultats inesperats.
Implementació de Multithreading en Unreal Engine
- Creant un Fil
Per crear un fil en Unreal Engine, utilitzarem la classe FRunnable
i FRunnableThread
. A continuació es mostra un exemple bàsic de com crear i executar un fil.
Exemple de Codi
// MyRunnable.h #pragma once #include "CoreMinimal.h" #include "HAL/Runnable.h" class MYPROJECT_API MyRunnable : public FRunnable { public: MyRunnable(); virtual ~MyRunnable(); virtual bool Init() override; virtual uint32 Run() override; virtual void Stop() override; private: FThreadSafeCounter StopTaskCounter; }; // MyRunnable.cpp #include "MyRunnable.h" MyRunnable::MyRunnable() { } MyRunnable::~MyRunnable() { } bool MyRunnable::Init() { // Inicialització del fil return true; } uint32 MyRunnable::Run() { // Codi que s'executarà en el fil while (StopTaskCounter.GetValue() == 0) { // Feina del fil } return 0; } void MyRunnable::Stop() { StopTaskCounter.Increment(); } // Creant i executant el fil FRunnable* Runnable = new MyRunnable(); FRunnableThread* Thread = FRunnableThread::Create(Runnable, TEXT("MyRunnableThread"));
- Sincronització amb Mutex
Per evitar condicions de carrera, utilitzarem un mutex per sincronitzar l'accés a les dades compartides.
Exemple de Codi
#include "HAL/ThreadSafeCounter.h" #include "HAL/ThreadSafeBool.h" #include "HAL/PlatformProcess.h" class MyRunnable : public FRunnable { public: MyRunnable(); virtual ~MyRunnable(); virtual bool Init() override; virtual uint32 Run() override; virtual void Stop() override; private: FThreadSafeCounter StopTaskCounter; FCriticalSection Mutex; int32 SharedData; }; bool MyRunnable::Init() { SharedData = 0; return true; } uint32 MyRunnable::Run() { while (StopTaskCounter.GetValue() == 0) { FScopeLock Lock(&Mutex); SharedData++; FPlatformProcess::Sleep(0.01f); // Simulant feina } return 0; } void MyRunnable::Stop() { StopTaskCounter.Increment(); }
- Errors Comuns i Consells
- Condicions de Carrera: Assegura't d'utilitzar mecanismes de sincronització com mutex per evitar condicions de carrera.
- Bloquejos: Evita bloquejar el fil principal del joc, ja que això pot afectar el rendiment i la resposta del joc.
- Gestió de Recursos: Assegura't de gestionar correctament els recursos del fil, com ara la memòria i els descriptors de fil.
Exercici Pràctic
Enunciat
Crea un fil que incrementi un comptador de manera segura utilitzant un mutex. El fil ha de parar quan el comptador arribi a 1000.
Solució
#include "HAL/ThreadSafeCounter.h" #include "HAL/PlatformProcess.h" class IncrementRunnable : public FRunnable { public: IncrementRunnable(); virtual ~IncrementRunnable(); virtual bool Init() override; virtual uint32 Run() override; virtual void Stop() override; private: FThreadSafeCounter StopTaskCounter; FCriticalSection Mutex; int32 Counter; }; IncrementRunnable::IncrementRunnable() : Counter(0) { } IncrementRunnable::~IncrementRunnable() { } bool IncrementRunnable::Init() { return true; } uint32 IncrementRunnable::Run() { while (StopTaskCounter.GetValue() == 0 && Counter < 1000) { FScopeLock Lock(&Mutex); Counter++; FPlatformProcess::Sleep(0.01f); // Simulant feina } return 0; } void IncrementRunnable::Stop() { StopTaskCounter.Increment(); } // Creant i executant el fil FRunnable* Runnable = new IncrementRunnable(); FRunnableThread* Thread = FRunnableThread::Create(Runnable, TEXT("IncrementRunnableThread"));
Conclusió
El multithreading és una tècnica poderosa que pot millorar significativament el rendiment del teu joc en Unreal Engine. En aquest mòdul, hem après els conceptes bàsics del multithreading, com crear i executar fils, i com sincronitzar l'accés a dades compartides per evitar condicions de carrera. Amb aquests coneixements, estaràs preparat per implementar multithreading en els teus projectes d'Unreal Engine.
Curs d'Unreal Engine
Mòdul 1: Introducció a Unreal Engine
- Què és Unreal Engine?
- Instal·lant Unreal Engine
- Navegant per la Interfície
- Creant el teu Primer Projecte
Mòdul 2: Conceptes Bàsics
Mòdul 3: Blueprints Intermedis
- Variables i Tipus de Dades
- Funcions i Esdeveniments
- Comunicació entre Blueprints
- Creant Objectes Interactius
Mòdul 4: Blueprints Avançats
Mòdul 5: Programació en C++ a Unreal Engine
- Configurant el teu Entorn de Desenvolupament
- Sintaxi Bàsica de C++
- Creant Classes en C++
- Integrant C++ amb Blueprints
Mòdul 6: Programació Avançada en C++
Mòdul 7: Temes Avançats
- Física i Col·lisió
- Renderització i Postprocessament
- Generació de Contingut Procedural
- Desenvolupament de Realitat Virtual