├── .gitignore ├── Content ├── BP_SimpleActor.uasset ├── BP_TestGameMode.uasset ├── Rama │ ├── BP_MultiThreadGameMode.uasset │ ├── BP_MyPlayerController.uasset │ ├── MultiThread.umap │ └── MultiThread_BuiltData.uasset ├── SimpleThread.umap └── SimpleThread_BuiltData.uasset ├── MultiThread ├── MultiThread1.png ├── MultiThread2.png ├── MultiThread3.png ├── MultiThread4.png ├── MultiThread5.png ├── MultiThread6.png ├── MultiThread7.png └── MultiThread8.jpg ├── README.md ├── Source ├── UE4_MultiThread.Target.cs ├── UE4_MultiThread │ ├── Private │ │ ├── MyPlayerController.cpp │ │ ├── PrimeNumberWorker.cpp │ │ ├── SimpleActor.cpp │ │ └── SimpleRunnable.cpp │ ├── Public │ │ ├── MyPlayerController.h │ │ ├── PrimeNumberWorker.h │ │ ├── SimpleActor.h │ │ └── SimpleRunnable.h │ ├── UE4_MultiThread.Build.cs │ ├── UE4_MultiThread.cpp │ ├── UE4_MultiThread.h │ ├── UE4_MultiThreadGameModeBase.cpp │ └── UE4_MultiThreadGameModeBase.h └── UE4_MultiThreadEditor.Target.cs ├── UE4_MultiThread.sln └── UE4_MultiThread.uproject /.gitignore: -------------------------------------------------------------------------------- 1 | /Saved 2 | /Binaries 3 | /Config 4 | /Intermediate 5 | /.vs 6 | -------------------------------------------------------------------------------- /Content/BP_SimpleActor.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/BP_SimpleActor.uasset -------------------------------------------------------------------------------- /Content/BP_TestGameMode.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/BP_TestGameMode.uasset -------------------------------------------------------------------------------- /Content/Rama/BP_MultiThreadGameMode.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/Rama/BP_MultiThreadGameMode.uasset -------------------------------------------------------------------------------- /Content/Rama/BP_MyPlayerController.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/Rama/BP_MyPlayerController.uasset -------------------------------------------------------------------------------- /Content/Rama/MultiThread.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/Rama/MultiThread.umap -------------------------------------------------------------------------------- /Content/Rama/MultiThread_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/Rama/MultiThread_BuiltData.uasset -------------------------------------------------------------------------------- /Content/SimpleThread.umap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/SimpleThread.umap -------------------------------------------------------------------------------- /Content/SimpleThread_BuiltData.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Content/SimpleThread_BuiltData.uasset -------------------------------------------------------------------------------- /MultiThread/MultiThread1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread1.png -------------------------------------------------------------------------------- /MultiThread/MultiThread2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread2.png -------------------------------------------------------------------------------- /MultiThread/MultiThread3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread3.png -------------------------------------------------------------------------------- /MultiThread/MultiThread4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread4.png -------------------------------------------------------------------------------- /MultiThread/MultiThread5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread5.png -------------------------------------------------------------------------------- /MultiThread/MultiThread6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread6.png -------------------------------------------------------------------------------- /MultiThread/MultiThread7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread7.png -------------------------------------------------------------------------------- /MultiThread/MultiThread8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/MultiThread/MultiThread8.jpg -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UE4_MultiThread 2 | UE4.24.3多线程 3 | 4 | 示例项目:https://github.com/tiax615/UE4_MultiThread 5 | 6 | ## 0. 背景 7 | UE4大部分工作都是在主线程里完成的,但有些事情可以放在多线程里,所以需要了解多线程。和渲染以及UObject/AActor的创建/修改/销毁有关的事请,应该在主线程完成,否则会崩溃。其他线程可能可以做网络数据收发和解析,数学运算等。 8 | 9 | 研究过程中发现Rama大佬写的官方Wiki代码比较复杂,其中FRunnable可以运行,但TaskGraphSystem跑不动。以下做一些简化和分析,理解不深,后续需要在使用过程中加深理解。 10 | 11 | ## 1. 项目结构 12 | ``` 13 | Content/ - 资源文件夹 14 | Rama/ - 官方Wiki尝试 15 | BP_MultiThreadGameMode 16 | BP_MyPlayerController 17 | BP_TestGameMode 18 | MultiThread 19 | BP_SimpleActor - 简化的实现 20 | SimpleThread - 简化的实现的地图 21 | Source/ - cpp文件夹 22 | Private/ 23 | MyPlayerController.cpp - 命名空间中实现TaskGraph,以及以上FRunnable的使用 24 | PrimeNumberWorker.cpp - 纯cpp类继承FRunnable 25 | SimpleActor.cpp - 使用SimpleRunnable的Actor类 26 | SimpleRunnable.cpp - FRunnable的一个简单应用 27 | ``` 28 | 29 | ## 2. 官方Wiki-Rama 30 | 大佬的代码,我看着有点吃力。 31 | ### 2.1. FRunnable 32 | 详见PrimeNumberWorker.h/PrimeNumberWorker.cpp 33 | ### 2.2. TaskGraph 34 | 详见MyPlayerController.h/MyPlayerController.cpp 35 | 36 | ## 3. FRunnable 37 | ### 3.1. 最简单的形式 38 | 最简单的FRunnable线程实现代码如下,这个能运行,每隔0.5s打印当前Count的值。但是因为只实现了Run(),并且永远循环,所以线程无法退出。就算游戏主线程停止了,这个线程还在继续运行。 39 | 40 | 如果Run()中的方法不是永远循环的,就可以直接退出。 41 | 42 | SimpleRunnable.h 43 | ``` 44 | #include "CoreMinimal.h" 45 | #include "HAL/Runnable.h" 46 | 47 | class FSimpleRunnable :public FRunnable 48 | { 49 | public: 50 | FSimpleRunnable(); 51 | ~FSimpleRunnable(); 52 | 53 | private: 54 | // 必须实现的几个 55 | virtual bool Init() override; 56 | virtual uint32 Run() override; 57 | virtual void Stop() override; 58 | virtual void Exit() override; 59 | }; 60 | ``` 61 | 62 | 只实现了一个简单的Run(),一直循环每隔0.5s打印Count的值,并且Count++。头文件PlayerController.h是为了使用FPlatformProcess::Sleep()。 63 | 64 | SimpleRunnable.cpp 65 | ``` 66 | #include "SimpleRunnable.h" 67 | #include "GameFramework\PlayerController.h" 68 | //#include "Windows\WindowsPlatformProcess.h" 69 | 70 | FSimpleRunnable::FSimpleRunnable() {} 71 | 72 | FSimpleRunnable::~FSimpleRunnable() {} 73 | 74 | bool FSimpleRunnable::Init() { return true; } 75 | 76 | uint32 FSimpleRunnable::Run() 77 | { 78 | int Count = 0; 79 | while (true) 80 | { 81 | UE_LOG(LogTemp, Warning, TEXT("%d"), Count++); 82 | FPlatformProcess::Sleep(0.5); 83 | } 84 | return 0; 85 | } 86 | 87 | void FSimpleRunnable::Stop() {} 88 | 89 | void FSimpleRunnable::Exit() {} 90 | ``` 91 | 92 | 以上实现了一个FRunnable,但还没有去调用,需要在主线程调用。这里新建了一个Actor类SimpleActor,在新增的RunSimpleRunnable()中调用。需要包含HAL/RunnableThread.h。 93 | 94 | SimpleActor.cpp 95 | ``` 96 | #include "SimpleActor.h" 97 | #include "HAL/RunnableThread.h" 98 | #include "SimpleRunnable.h" 99 | 100 | //... 101 | 102 | void ASimpleActor::RunSimpleRunnable() 103 | { 104 | FRunnable* SimpleRunnable = new FSimpleRunnable(); 105 | FRunnableThread* SimpleRunnableThread = FRunnableThread::Create(SimpleRunnable, TEXT("MySimpleRunnable")); 106 | } 107 | ``` 108 | 109 | 使用的时候出现一个奇怪的现象,每次打印的不是连续的整数,而是每个数都多加了1。可能是UE_LOG里,Count++执行了两次。 110 | ![最简单形式的运行结果](./MultiThread/MultiThread1.png) 111 | 112 | ### 3.2. 改进 113 | #### 3.2.1. 创建和释放 114 | 以上最简单的形式,只是能把FRunnable用起来,还差很多东西,参考官方Wiki稍作说明和简化。 115 | 116 | 为了方便创建线程,在SimpleRunnable类中定义静态单例MySimpleRunnable,这个线程只能创建一次。和存放线程的指针MyRunnableThread,以便在合适的时候释放掉。UE4中纯C++类需要手动管理内存。 117 | 118 | 包含头文件HAL/Runnable.h和HAL/RunnableThread.h。初始化MySimpleRunnable为nullptr,在构造函数中Create这个MyRunnableThread线程,在析构函数中delete。 119 | ``` 120 | static FSimpleRunnable* MySimpleRunnable; 121 | class FRunnableThread* MyRunnableThread; 122 | 123 | FSimpleRunnable* FSimpleRunnable::MySimpleRunnable = nullptr; 124 | 125 | FSimpleRunnable::FSimpleRunnable() 126 | { 127 | MyRunnableThread = FRunnableThread::Create(this, TEXT("MySimpleRunnable")); 128 | } 129 | 130 | FSimpleRunnable::~FSimpleRunnable() 131 | { 132 | delete MyRunnableThread; 133 | MyRunnableThread = nullptr; 134 | } 135 | ``` 136 | 137 | 再新增一个静态方法JoyInit,只要包含了SimpleRunnable.h,在任何地方调用都能创建一个SimpleRunnable线程单例。当MySimpleRunnable是空指针并且当前平台支持多线程时,创建新的SimpleRunnable实例并让MySimpleRunnable指向它。 138 | ``` 139 | static FSimpleRunnable* JoyInit(); 140 | 141 | FSimpleRunnable* FSimpleRunnable::JoyInit() 142 | { 143 | if (!MySimpleRunnable && FPlatformProcess::SupportsMultithreading()) 144 | { 145 | MySimpleRunnable = new FSimpleRunnable(); 146 | } 147 | } 148 | ``` 149 | 150 | #### 3.2.2. 退出 151 | 那怎么退出呢。如果Run中的代码执行完了,会自动执行Stop和Exit退出,否则就需要手动去中断。这里使用FThreadSafeCounter这个计数去判断,是否还要继续执行Run的方法,当计数不为0时,结束Run。 152 | 153 | 在构造时初始化计数StopTaskCounter,将Run中的while(true)改为While(StopTaskCounter.GetValue()==0)。在主线程中,调用SimpleRunnable类的shutdown()就可以主动退出 154 | ``` 155 | FThreadSafeCounter StopTaskCounter; 156 | void EnsureCompletion(); 157 | static void Shutdown(); 158 | 159 | FSimpleRunnable::FSimpleRunnable():StopTaskCounter(0) 160 | { 161 | MyRunnableThread = FRunnableThread::Create(this, TEXT("MySimpleRunnable")); 162 | } 163 | 164 | void FSimpleRunnable::EnsureCompletion() 165 | { 166 | Stop(); 167 | MyRunnableThread->WaitForCompletion(); 168 | } 169 | 170 | uint32 FSimpleRunnable::Run() 171 | { 172 | UE_LOG(LogTemp, Warning, TEXT("SimpleRunnable Run")); 173 | 174 | int Count = 0; 175 | while (StopTaskCounter.GetValue()==0) 176 | { 177 | UE_LOG(LogTemp, Warning, TEXT("%d"), Count); 178 | Count++; 179 | FPlatformProcess::Sleep(0.5); 180 | } 181 | return 0; 182 | } 183 | 184 | void FSimpleRunnable::Stop() 185 | { 186 | UE_LOG(LogTemp, Warning, TEXT("SimpleRunnable Stop")); 187 | StopTaskCounter.Increment(); 188 | } 189 | 190 | void FSimpleRunnable::Shutdown() 191 | { 192 | if (MySimpleRunnable) 193 | { 194 | MySimpleRunnable->EnsureCompletion(); 195 | delete MySimpleRunnable; 196 | MySimpleRunnable = nullptr; 197 | } 198 | } 199 | ``` 200 | 201 | #### 3.2.3. 使用 202 | 还是在SimpleActor中,实现SimRunnable的使用。 203 | 204 | SimpleActor.h,声明蓝图可调用的方法RunSimpleRunnable()和StopSimpleRunnable(),用于开启和停止SimpleRunnable线程。 205 | ``` 206 | public: 207 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 208 | void RunSimpleRunnable(); 209 | 210 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 211 | void StopSimpleRunnable(); 212 | ``` 213 | 214 | SimpleActor.cpp,头文件只需要包含SimpleRunnable.h,通过静态方法开启和停止,使用方法非常简单。 215 | ``` 216 | void ASimpleActor::RunSimpleRunnable() 217 | { 218 | FSimpleRunnable::JoyInit(); 219 | } 220 | 221 | void ASimpleActor::StopSimpleRunnable() 222 | { 223 | FSimpleRunnable::Shutdown(); 224 | } 225 | ``` 226 | 227 | 新建一个继承自SimpleActor类的蓝图类BP_SimpleActor,EventBeginPlay时使用RunSimpleRunnable,EventEndPlay时使用 228 | StopSimpleRunnable。 229 | 230 | ![BP_SimpleActor使用SimpleRunnable](./MultiThread/MultiThread2.png) 231 | 232 | Play的时候正常开启了线程,结束游戏的时候也停止了下来。 233 | 234 | ![改进的运行结果](./MultiThread/MultiThread3.png) 235 | 236 | ## 4. TaskGraph 237 | ### 4.1. 命名空间 238 | TaskGraph系统是UE4一套抽象的异步任务处理系统,可以创建多个多线程任务。官方Wiki是使用命名空间来做的,这里我简化一下也写了个命名空间,可以用来打印指定个数的整数。 239 | 240 | namespace SimpleTaskGraph,内容见代码的注释,这段代码在SimpleActor.cpp中。 241 | ``` 242 | namespace SimpleTaskGraph 243 | { 244 | FGraphEventArray SimpleTask_CompletionEvents; // 用于保存任务的数组 245 | int Number = 0; // 要打印的整数,从0开始 246 | 247 | // Are all tasks complete? 248 | bool TasksAreComplete() 249 | { 250 | for (int32 Index = 0; Index < SimpleTask_CompletionEvents.Num(); Index++) 251 | { 252 | if (!SimpleTask_CompletionEvents[Index]->IsComplete()) 253 | { 254 | return false; 255 | } 256 | } 257 | return true; 258 | } 259 | 260 | // 打印下一个整数 261 | int GetNextInt() 262 | { 263 | FPlatformProcess::Sleep(0.1); 264 | Number++; 265 | UE_LOG(LogTemp, Warning, TEXT("SimpleTask %d"), Number); 266 | return Number; 267 | } 268 | 269 | // Each task thread 270 | class SimpleTask 271 | { 272 | public: 273 | SimpleTask() {} 274 | 275 | // 获取任务的名字,暂时没用到 276 | static const TCHAR* GetTaskName() 277 | { 278 | return TEXT("SimpleTask"); 279 | } 280 | 281 | ////////////////////////////////////////////////////////////////////////// 282 | // 需要实现的方法,没有这些会编译报错。暂时没用到 283 | FORCEINLINE static TStatId GetStatId() 284 | { 285 | RETURN_QUICK_DECLARE_CYCLE_STAT(SimpleTask, STATGROUP_TaskGraphTasks); 286 | } 287 | static ESubsequentsMode::Type GetSubsequentsMode() 288 | { 289 | return ESubsequentsMode::TrackSubsequents; 290 | } 291 | static ENamedThreads::Type GetDesiredThread() 292 | { 293 | return ENamedThreads::AnyThread; 294 | } 295 | ////////////////////////////////////////////////////////////////////////// 296 | 297 | // Main function: DO Task! 298 | void DoTask(ENamedThreads::Type CurrentThread, const FGraphEventRef& MyCompletionGraphEvent) 299 | { 300 | GetNextInt(); 301 | } 302 | }; 303 | 304 | // 使用命名空间SimpleTaskGraph的这个方法,开启任务图 305 | void GetInts(const uint32 TotalToGet) 306 | { 307 | for (uint32 i = 0; i < TotalToGet; i++) 308 | { 309 | // 第一个变量等待其中的任务完成,然后再进行下一个任务 310 | // 第一个变量官方Wiki是NULL,这样可以不按照顺序执行 311 | SimpleTask_CompletionEvents.Add(TGraphTask::CreateTask( 312 | &SimpleTask_CompletionEvents, ENamedThreads::GameThread).ConstructAndDispatchWhenReady()); 313 | } 314 | } 315 | } 316 | ``` 317 | 318 | ### 4.2. 使用 319 | 这里参照了官方Wiki的写法,在StartTask()中开启任务图,用计时器每隔1s检查是否所有任务执行完了。在CheckAllTasksDone()中,如果所有任务执行完了,也就是SimpleTaskGraph::TasksAreComplete()==true,就清除StartTask()中开启的计时器,并且打印任务图完成。 320 | 321 | SimpleActor.h,声明计时器句柄,开启任务图和检查任务图是否完成的方法。 322 | ``` 323 | public: 324 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 325 | void StartTask(int TotalToGet); 326 | 327 | private: 328 | void CheckAllTasksDone(); 329 | 330 | FTimerHandle MyTimerHandle; 331 | ``` 332 | 333 | SimpleActor.cpp,需要包含头文件TimerManager.h 334 | ``` 335 | void ASimpleActor::StartTask(int TotalToGet) 336 | { 337 | SimpleTaskGraph::GetInts(TotalToGet); 338 | GetWorldTimerManager().SetTimer(MyTimerHandle, this, &ASimpleActor::CheckAllTasksDone, 1, true); 339 | } 340 | 341 | void ASimpleActor::CheckAllTasksDone() 342 | { 343 | if (SimpleTaskGraph::TasksAreComplete()) 344 | { 345 | GetWorldTimerManager().ClearTimer(MyTimerHandle); 346 | UE_LOG(LogTemp, Warning, TEXT("TaskGraph Done!")); 347 | } 348 | } 349 | ``` 350 | 351 | 在蓝图BP_SimpleActor中,EventBeginPlay后连上StartTask,开启任务图,和之前的SimpleRunnable可以比较一下。 352 | 353 | ![开启任务图](./MultiThread/MultiThread4.png) 354 | 355 | 运行结果如下,可见SimpleTask按照顺序进行,完成之后打印了 TaskGraph Done! 356 | 357 | ![任务图SimpleTaskGraph运行结果](./MultiThread/MultiThread5.png) 358 | 359 | ## 5. AsyncTasks 360 | 除了以上的“标准多线程”FRunnable和任务图系统TaskGraph,还有AsyncTasks也能实现不阻塞主线程而异步干事情。我们可以使用FAsyncTask或者FAutoDeleteAsyncTask。使用FAsyncTask 时,我们需要手动停止或删除任务;使用FAutoDeleteAsyncTask时,系统则会自动在任务结束后,删除任务。 361 | 362 | ### 5.1. 类 363 | 这次在SimpleActor中新建了一个类FSimpleAsyncTasks,继承自FNonAbandonableTask类,友元类FAutoDeleteAsyncTask。 364 | 365 | SimpleActor.h中FSimpleAsyncTasks类的声明如下,可以接收两个输入。 366 | ``` 367 | class FSimpleAsyncTasks : public FNonAbandonableTask 368 | { 369 | friend class FAutoDeleteAsyncTask; 370 | public: 371 | FSimpleAsyncTasks(int32 Input1, int32 Input2); 372 | 373 | protected: 374 | int32 MyInput1; 375 | int32 MyInput2; 376 | void DoWork(); 377 | FORCEINLINE TStatId GetStatId() const; 378 | }; 379 | ``` 380 | 381 | SimpleActor.cpp中的实现如下,GetStatId()没有直接使用,但是必须的,类似TaskGraph。DoWork()中实现业务逻辑,这里每隔0.2s打印一个从1递增的整数直到5 382 | ``` 383 | FSimpleAsyncTasks::FSimpleAsyncTasks(int32 Input1, int32 Input2) : 384 | MyInput1(Input1), 385 | MyInput2(Input2) 386 | { 387 | } 388 | 389 | void FSimpleAsyncTasks::DoWork() 390 | { 391 | for (int i = 1; i < 6; i++) 392 | { 393 | UE_LOG(LogTemp, Warning, TEXT("SimpleAsyncTasks Dowork %d"),i); 394 | FPlatformProcess::Sleep(0.2); 395 | } 396 | } 397 | 398 | FORCEINLINE TStatId FSimpleAsyncTasks::GetStatId() const 399 | { 400 | RETURN_QUICK_DECLARE_CYCLE_STAT(FSimpleAsyncTasks, STATGROUP_ThreadPoolAsyncTasks); 401 | } 402 | ``` 403 | 404 | ### 5.2. 使用 405 | 同样在SimpleActor中使用,新增方法StartAsyncTask()。 406 | ``` 407 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 408 | void StartAsyncTask(); 409 | 410 | void ASimpleActor::StartAsyncTask() 411 | { 412 | // Instantiate a copy of the actual task, and queue the task for execution with StartBackgroundTask() 413 | (new FAutoDeleteAsyncTask(6,6))->StartBackgroundTask(); 414 | } 415 | ``` 416 | 417 | 同样在蓝图BP_SimpleActor的EventBeginPlay中连上StartAsyncTask,运行结果如下。可以看到FRunnable,TaskGraph,AsyncTasks各自异步运行,没有影响到主线程。 418 | 419 | ![开启异步任务](./MultiThread/MultiThread6.png) 420 | 421 | ![异步任务运行结果](./MultiThread/MultiThread7.png) 422 | 423 | ### 6. 总结 424 | FRunnable“标准”多线程,会带来更多的开销,但适合长期连续的操作。 425 | 426 | TaskGraph和AsyncTasks是对已有线程的复用。 427 | 428 | TaskGraph不适合做计算量大的操作,可能带来严重的卡顿,因为Tick是在TaskGraph中做的。同时也可以将任务分给其他线程执行,可以设置任务依赖顺序。 429 | 430 | AsyncTasks类似TaskGraph,但更简洁。AsyncTask系统实现的多线程与你自己继承FRunnable实现的原理相似,不过他在用法上比较简单,而且还可以直接借用UE4提供的线程池,很方便。 431 | 432 | 借用知乎大佬的图,描述了多线程相关类的关系,可以参考。 433 | 434 | ![MultiThread](./MultiThread/MultiThread8.jpg) 435 | 436 | ## 999. 参考资料 437 | 1. 官方Wiki,作者Rama https://www.ue4community.wiki/Legacy/Multi-Threading:_How_to_Create_Threads_in_UE4 438 | 2. 官方Wiki,作者Rama https://www.ue4community.wiki/Legacy/Multi-Threading:_Task_Graph_System 439 | 3. 官方Wiki,作者不详 https://www.ue4community.wiki/Legacy/Using_AsyncTasks 440 | 3. UE4 C++基础教程-多线程,蓝子悠悠 https://zhuanlan.zhihu.com/p/133921916 441 | 4. 《Exploring in UE4》多线程机制详解[原理分析] https://zhuanlan.zhihu.com/p/38881269 442 | 5. https://www.cnblogs.com/mcomco/p/11316803.html -------------------------------------------------------------------------------- /Source/UE4_MultiThread.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class UE4_MultiThreadTarget : TargetRules 7 | { 8 | public UE4_MultiThreadTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Game; 11 | DefaultBuildSettings = BuildSettingsVersion.V2; 12 | ExtraModuleNames.AddRange( new string[] { "UE4_MultiThread" } ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Private/MyPlayerController.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Source/UE4_MultiThread/Private/MyPlayerController.cpp -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Private/PrimeNumberWorker.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Source/UE4_MultiThread/Private/PrimeNumberWorker.cpp -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Private/SimpleActor.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Source/UE4_MultiThread/Private/SimpleActor.cpp -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Private/SimpleRunnable.cpp: -------------------------------------------------------------------------------- 1 | #include "SimpleRunnable.h" 2 | #include "HAL/RunnableThread.h" 3 | #include "GameFramework\PlayerController.h" 4 | //#include "Windows\WindowsPlatformProcess.h" 5 | 6 | FSimpleRunnable* FSimpleRunnable::MySimpleRunnable = nullptr; 7 | 8 | FSimpleRunnable::FSimpleRunnable():StopTaskCounter(0) 9 | { 10 | MyRunnableThread = FRunnableThread::Create(this, TEXT("MySimpleRunnable")); 11 | } 12 | 13 | FSimpleRunnable::~FSimpleRunnable() 14 | { 15 | delete MyRunnableThread; 16 | MyRunnableThread = nullptr; 17 | } 18 | 19 | FSimpleRunnable* FSimpleRunnable::JoyInit() 20 | { 21 | if (!MySimpleRunnable && FPlatformProcess::SupportsMultithreading()) 22 | { 23 | MySimpleRunnable = new FSimpleRunnable(); 24 | } 25 | return MySimpleRunnable; 26 | } 27 | 28 | void FSimpleRunnable::EnsureCompletion() 29 | { 30 | Stop(); 31 | MyRunnableThread->WaitForCompletion(); 32 | } 33 | 34 | void FSimpleRunnable::Shutdown() 35 | { 36 | if (MySimpleRunnable) 37 | { 38 | MySimpleRunnable->EnsureCompletion(); 39 | delete MySimpleRunnable; 40 | MySimpleRunnable = nullptr; 41 | } 42 | } 43 | 44 | bool FSimpleRunnable::Init() 45 | { 46 | UE_LOG(LogTemp, Warning, TEXT("SimpleRunnable Init")); 47 | return true; 48 | } 49 | 50 | uint32 FSimpleRunnable::Run() 51 | { 52 | UE_LOG(LogTemp, Warning, TEXT("SimpleRunnable Run")); 53 | 54 | int Count = 0; 55 | while (StopTaskCounter.GetValue()==0) 56 | { 57 | UE_LOG(LogTemp, Warning, TEXT("%d"), Count); 58 | Count++; 59 | FPlatformProcess::Sleep(0.5); 60 | } 61 | return 0; 62 | } 63 | 64 | void FSimpleRunnable::Stop() 65 | { 66 | UE_LOG(LogTemp, Warning, TEXT("SimpleRunnable Stop")); 67 | StopTaskCounter.Increment(); 68 | } 69 | 70 | void FSimpleRunnable::Exit() 71 | { 72 | UE_LOG(LogTemp, Warning, TEXT("SimpleRunnable Exit")); 73 | } 74 | 75 | -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Public/MyPlayerController.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Source/UE4_MultiThread/Public/MyPlayerController.h -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Public/PrimeNumberWorker.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Source/UE4_MultiThread/Public/PrimeNumberWorker.h -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Public/SimpleActor.h: -------------------------------------------------------------------------------- 1 | // Fill out your copyright notice in the Description page of Project Settings. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/Actor.h" 7 | #include "SimpleActor.generated.h" 8 | 9 | UCLASS() 10 | class UE4_MULTITHREAD_API ASimpleActor : public AActor 11 | { 12 | GENERATED_BODY() 13 | 14 | public: 15 | // Sets default values for this actor's properties 16 | ASimpleActor(); 17 | 18 | protected: 19 | // Called when the game starts or when spawned 20 | virtual void BeginPlay() override; 21 | 22 | public: 23 | // Called every frame 24 | virtual void Tick(float DeltaTime) override; 25 | 26 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 27 | void RunSimpleRunnable(); 28 | 29 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 30 | void StopSimpleRunnable(); 31 | 32 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 33 | void StartTaskGraph(int TotalToGet); 34 | 35 | UFUNCTION(BlueprintCallable, Category = "SimpleActor") 36 | void StartAsyncTask(); 37 | 38 | private: 39 | void CheckAllTasksDone(); 40 | 41 | FTimerHandle MyTimerHandle; 42 | }; 43 | 44 | class FSimpleAsyncTasks : public FNonAbandonableTask 45 | { 46 | friend class FAutoDeleteAsyncTask; 47 | public: 48 | FSimpleAsyncTasks(int32 Input1, int32 Input2); 49 | 50 | protected: 51 | int32 MyInput1; 52 | int32 MyInput2; 53 | void DoWork(); 54 | FORCEINLINE TStatId GetStatId() const; 55 | }; -------------------------------------------------------------------------------- /Source/UE4_MultiThread/Public/SimpleRunnable.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tiax615/UE4_MultiThread/19fca1ae67955f022e9a8d427cd998f4d6624e2a/Source/UE4_MultiThread/Public/SimpleRunnable.h -------------------------------------------------------------------------------- /Source/UE4_MultiThread/UE4_MultiThread.Build.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class UE4_MultiThread : ModuleRules 6 | { 7 | public UE4_MultiThread(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | 11 | PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore" }); 12 | 13 | PrivateDependencyModuleNames.AddRange(new string[] { }); 14 | 15 | // Uncomment if you are using Slate UI 16 | // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); 17 | 18 | // Uncomment if you are using online features 19 | // PrivateDependencyModuleNames.Add("OnlineSubsystem"); 20 | 21 | // To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Source/UE4_MultiThread/UE4_MultiThread.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #include "UE4_MultiThread.h" 4 | #include "Modules/ModuleManager.h" 5 | 6 | IMPLEMENT_PRIMARY_GAME_MODULE( FDefaultGameModuleImpl, UE4_MultiThread, "UE4_MultiThread" ); 7 | -------------------------------------------------------------------------------- /Source/UE4_MultiThread/UE4_MultiThread.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | 7 | -------------------------------------------------------------------------------- /Source/UE4_MultiThread/UE4_MultiThreadGameModeBase.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | 4 | #include "UE4_MultiThreadGameModeBase.h" 5 | 6 | -------------------------------------------------------------------------------- /Source/UE4_MultiThread/UE4_MultiThreadGameModeBase.h: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "GameFramework/GameModeBase.h" 7 | #include "UE4_MultiThreadGameModeBase.generated.h" 8 | 9 | /** 10 | * 11 | */ 12 | UCLASS() 13 | class UE4_MULTITHREAD_API AUE4_MultiThreadGameModeBase : public AGameModeBase 14 | { 15 | GENERATED_BODY() 16 | 17 | }; 18 | -------------------------------------------------------------------------------- /Source/UE4_MultiThreadEditor.Target.cs: -------------------------------------------------------------------------------- 1 | // Copyright 1998-2019 Epic Games, Inc. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | using System.Collections.Generic; 5 | 6 | public class UE4_MultiThreadEditorTarget : TargetRules 7 | { 8 | public UE4_MultiThreadEditorTarget( TargetInfo Target) : base(Target) 9 | { 10 | Type = TargetType.Editor; 11 | DefaultBuildSettings = BuildSettingsVersion.V2; 12 | ExtraModuleNames.AddRange( new string[] { "UE4_MultiThread" } ); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /UE4_MultiThread.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.25807.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Engine", "Engine", "{ADDA8AF5-244E-41BD-BB46-DFFDC328056D}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Games", "Games", "{FA99F01E-74CC-4F8B-9332-C9AA153977C9}" 9 | EndProject 10 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UE4", "Intermediate\ProjectFiles\UE4.vcxproj", "{D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}" 11 | EndProject 12 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UE4_MultiThread", "Intermediate\ProjectFiles\UE4_MultiThread.vcxproj", "{FD8C83C0-E902-447F-AB2B-0633F50058BA}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Visualizers", "Visualizers", "{037D095C-631E-40B8-8D1A-23C79381187A}" 15 | ProjectSection(SolutionItems) = preProject 16 | D:\Program Files\Epic Games\UE_4.24\Engine\Extras\VisualStudioDebugging\UE4.natvis = D:\Program Files\Epic Games\UE_4.24\Engine\Extras\VisualStudioDebugging\UE4.natvis 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | DebugGame Editor|Android = DebugGame Editor|Android 22 | DebugGame Editor|Linux = DebugGame Editor|Linux 23 | DebugGame Editor|LinuxAArch64 = DebugGame Editor|LinuxAArch64 24 | DebugGame Editor|Win32 = DebugGame Editor|Win32 25 | DebugGame Editor|Win64 = DebugGame Editor|Win64 26 | DebugGame|Android = DebugGame|Android 27 | DebugGame|Linux = DebugGame|Linux 28 | DebugGame|LinuxAArch64 = DebugGame|LinuxAArch64 29 | DebugGame|Win32 = DebugGame|Win32 30 | DebugGame|Win64 = DebugGame|Win64 31 | Development Editor|Android = Development Editor|Android 32 | Development Editor|Linux = Development Editor|Linux 33 | Development Editor|LinuxAArch64 = Development Editor|LinuxAArch64 34 | Development Editor|Win32 = Development Editor|Win32 35 | Development Editor|Win64 = Development Editor|Win64 36 | Development|Android = Development|Android 37 | Development|Linux = Development|Linux 38 | Development|LinuxAArch64 = Development|LinuxAArch64 39 | Development|Win32 = Development|Win32 40 | Development|Win64 = Development|Win64 41 | Shipping|Android = Shipping|Android 42 | Shipping|Linux = Shipping|Linux 43 | Shipping|LinuxAArch64 = Shipping|LinuxAArch64 44 | Shipping|Win32 = Shipping|Win32 45 | Shipping|Win64 = Shipping|Win64 46 | EndGlobalSection 47 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 48 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame Editor|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32 49 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame Editor|Linux.ActiveCfg = BuiltWithUnrealBuildTool|Win32 50 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame Editor|LinuxAArch64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 51 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32 52 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 53 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32 54 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame|Linux.ActiveCfg = BuiltWithUnrealBuildTool|Win32 55 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame|LinuxAArch64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 56 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32 57 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.DebugGame|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 58 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development Editor|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32 59 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development Editor|Linux.ActiveCfg = BuiltWithUnrealBuildTool|Win32 60 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development Editor|LinuxAArch64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 61 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development Editor|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32 62 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development Editor|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 63 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32 64 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development|Linux.ActiveCfg = BuiltWithUnrealBuildTool|Win32 65 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development|LinuxAArch64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 66 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32 67 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Development|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 68 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Shipping|Android.ActiveCfg = BuiltWithUnrealBuildTool|Win32 69 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Shipping|Linux.ActiveCfg = BuiltWithUnrealBuildTool|Win32 70 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Shipping|LinuxAArch64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 71 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Shipping|Win32.ActiveCfg = BuiltWithUnrealBuildTool|Win32 72 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7}.Shipping|Win64.ActiveCfg = BuiltWithUnrealBuildTool|Win32 73 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|Android.ActiveCfg = Invalid|Win32 74 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|Linux.ActiveCfg = Linux_DebugGame_Editor|Win32 75 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|Linux.Build.0 = Linux_DebugGame_Editor|Win32 76 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|LinuxAArch64.ActiveCfg = Invalid|Win32 77 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|Win32.ActiveCfg = Invalid|Win32 78 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|Win64.ActiveCfg = DebugGame_Editor|x64 79 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame Editor|Win64.Build.0 = DebugGame_Editor|x64 80 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Android.ActiveCfg = Android_DebugGame|Win32 81 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Android.Build.0 = Android_DebugGame|Win32 82 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Linux.ActiveCfg = Linux_DebugGame|Win32 83 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Linux.Build.0 = Linux_DebugGame|Win32 84 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|LinuxAArch64.ActiveCfg = LinuxAArch64_DebugGame|Win32 85 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|LinuxAArch64.Build.0 = LinuxAArch64_DebugGame|Win32 86 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Win32.ActiveCfg = DebugGame|Win32 87 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Win32.Build.0 = DebugGame|Win32 88 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Win64.ActiveCfg = DebugGame|x64 89 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.DebugGame|Win64.Build.0 = DebugGame|x64 90 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|Android.ActiveCfg = Invalid|Win32 91 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|Linux.ActiveCfg = Linux_Development_Editor|Win32 92 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|Linux.Build.0 = Linux_Development_Editor|Win32 93 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|LinuxAArch64.ActiveCfg = Invalid|Win32 94 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|Win32.ActiveCfg = Invalid|Win32 95 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|Win64.ActiveCfg = Development_Editor|x64 96 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development Editor|Win64.Build.0 = Development_Editor|x64 97 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Android.ActiveCfg = Android_Development|Win32 98 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Android.Build.0 = Android_Development|Win32 99 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Linux.ActiveCfg = Linux_Development|Win32 100 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Linux.Build.0 = Linux_Development|Win32 101 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|LinuxAArch64.ActiveCfg = LinuxAArch64_Development|Win32 102 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|LinuxAArch64.Build.0 = LinuxAArch64_Development|Win32 103 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Win32.ActiveCfg = Development|Win32 104 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Win32.Build.0 = Development|Win32 105 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Win64.ActiveCfg = Development|x64 106 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Development|Win64.Build.0 = Development|x64 107 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Android.ActiveCfg = Android_Shipping|Win32 108 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Android.Build.0 = Android_Shipping|Win32 109 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Linux.ActiveCfg = Linux_Shipping|Win32 110 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Linux.Build.0 = Linux_Shipping|Win32 111 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|LinuxAArch64.ActiveCfg = LinuxAArch64_Shipping|Win32 112 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|LinuxAArch64.Build.0 = LinuxAArch64_Shipping|Win32 113 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Win32.ActiveCfg = Shipping|Win32 114 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Win32.Build.0 = Shipping|Win32 115 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Win64.ActiveCfg = Shipping|x64 116 | {FD8C83C0-E902-447F-AB2B-0633F50058BA}.Shipping|Win64.Build.0 = Shipping|x64 117 | EndGlobalSection 118 | GlobalSection(SolutionProperties) = preSolution 119 | HideSolutionNode = FALSE 120 | EndGlobalSection 121 | GlobalSection(NestedProjects) = preSolution 122 | {D7A207CF-4A6D-4AF4-8F25-2382B7C327C7} = {ADDA8AF5-244E-41BD-BB46-DFFDC328056D} 123 | {FD8C83C0-E902-447F-AB2B-0633F50058BA} = {FA99F01E-74CC-4F8B-9332-C9AA153977C9} 124 | EndGlobalSection 125 | EndGlobal 126 | -------------------------------------------------------------------------------- /UE4_MultiThread.uproject: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "EngineAssociation": "4.24", 4 | "Category": "", 5 | "Description": "", 6 | "Modules": [ 7 | { 8 | "Name": "UE4_MultiThread", 9 | "Type": "Runtime", 10 | "LoadingPhase": "Default", 11 | "AdditionalDependencies": [ 12 | "Engine" 13 | ] 14 | } 15 | ] 16 | } --------------------------------------------------------------------------------