Search code examples
c++multithreadingqueueunreal-engine4

Best Design Practices for Passing Around Multithread Data?


I'm trying to create a clean and efficient design for passing off events to a background thread for evaluation, then return a selected result to the game thread.

This is my initial design

//Occurrence object passed from director on game thread to background thread OccurrenceQueue

    //Execute BackgroundThread::EvaluateQueues()

        //If OccurrenceQueue.Dequeue()

            //Score Occurrence, then return via Occurrence.GetOwner()->PurposeSelected(Occurrence)

            //EvaluateQueues()

This resulted in a clean loop of selecting a chain of purposes from an event. So now I want to move this to a background thread. Here is what I've learned so far:

  • Thread Safety (in UE) requires absolutely no modification of UObject data from other threads (From what I read this is due to their custom GC)

    • You can lock objects and/or design so that objects in background
      thread aren't touched by game thread, but there is still a risk of
      unexpected behavior due to lifetime not being extended by background thread and synchonization issues
  • You cannot simply execute a function on a game thread existing object to move the callstack back to game thread

    • Calling Occurrence.GetOwner()->PurposeSelected(Occurrence) from a background thread remains in the background thread

      • This is the main subject I'd like to get a better understanding of
    • This applies to delegates in UE as well

  • TQueues in UE can be used across threads safely

From what I've learned above, my current design doesn't appear to be logically possible.

These are my alternatives thus far:

  • Use two queues

    • One to dequeue and score on the background thread

    • The other to dequeue the result on game thread via tick

  • Use a delegate existing on the game thread, which calls OccurrenceEvaluated.Broadcast() through tick

    • When a result is scored, bind Occurrence.GetOwner()->PurposeSelected(Occurrence) to OccurrenceEvaluated
  • I've seen that c++ utilizes something called Future() (or something like that) for ASync tasks, and it appears UE has something similar with TFuture<> && TFuture.IsReady(), but I have yet to look deeper into that and how it returns data

    • Same with FAsyncTask

I'm hesitant to implement any design which utilizes tick to check if data has been updated/returned from background threads.

Can anyone suggest relevant design practices, or clarify the nature of returning execution to a main thread from a background thread (I've had a hard time finding the right question to research/info regarding this)?


Solution

  • I found a perfect solution. As these events aren't particularly time sensitive, I just use Unreal Engine's AsyncTask() to schedule an async task on the game thread from my background thread. As @Pepjin Kramer pointed out is the same as std::async.

    So simple it's basically a slap in the face.