Returning a reference from a Thread Task

I’m multi-threading our AI to choose the closest actor. To do this I’m trying to use a task on the task graph since there can be hundreds of units doing this at once.

I created a class to house all the information for the thread. It looks like this.

struct FUnitLocationData
{
	FVector myLocation;
	TArray<AActor*> otherUnits;
	AController *MyAIController;
	
	FUnitLocationData()
	{
		myLocation = { 0,0,0 };
		otherUnits.Empty();		
	}
};


class FUnitUpdateObject
{
public:
	AController *AIController;
	AActor* ClosestActor;
	float distanceToActor;
	FGraphEventArray UnitUpdateThread_CompletionEvents;

	bool TasksCompleted();

	void FindClosestEnemy();
};

My testing shows that I can’t use references at all because if the unit dies and the thread continues, I get null references. All I need to do is set 2 blackboard values.

BBComp->SetValueAsObject("ClosestTarget", UnitUpdater.ClosestActor);
BBComp->SetValueAsFloat("DistanceToTarget", this->UnitUpdater.distanceToActor);

But because the AIController owning the thread is destroyed, The thread crashes the game. Where should I be storing variables that makes them unique to the querying unit but owned by the querying unit.

I am not experienced in multi threading, but could you have the owner keep track if he is running a thread or not? And if he is he should wait until completion and then destroy itself.

In UE4 all the gameplay code (including AI) is expected to live on the GameThread. You’ll get yourself into a lot of trouble if you try calling functions on gamethread-bound objects outside of gamethread (unless you really, relly know what you’re doing).

Ideally all the computation you’d like to do outside of game-thread should be abstracted from any UObject code. You should gather all the information you’d might want to ask off-gamethread, pack it into your tasks, and have tasks operate on that data, without any need to ask for additional information. This has lots of benefits (apart from being thread-safe) - it’s a lot more efficient to ask actor X for a location, store it and reuse it than have every separate task ask X for its location (even if it was possible).

Alternatively you can create a gamethread-side model of the world and use thread shared pointers (TSharedPtr< FYourType, ESPMode::ThreadSafe>) or references (TSharedRef< FYourType, ESPMode::ThreadSafe>) to access it from threads - again, note that TSharedPtr doesn’t work with UObjects, you’ll need to switch over to regular C++ to make it work safely and reliably.

Cheers,

–mieszko

Hi Mieszko, Your second point of gathering all the data and doing the calculations to the side is exactly what I’d like to do. The trouble is updating the actors information after the tread is complete. I’m not sure how to associate the result with the appropriate actor.

After sleeping on it maybe I can use a TMap Where the Actor Reference is never used in the thread and only there to associate LocationData with the actor when the task completes. It kind of feels like I’m flirting with the death a bit but we will see.

Yeah, maps or array that translate index to an actor. It’s not rocket science, it’s just coding under constraints :slight_smile:

Just wanted to say thanks your your assistance. I managed to shave off a very substantial 100ms from my game thread. Apparently 300 AIs fighting in an all out brawl is a bit heavy on the game thread.