Modifying UObjects in FRunnable threads

Hi. This is more of a conceptional question arising out of my lack of experience with multi-threading in c++

I wanted to create a new thread to handle heavy AI calculations. I found some info and guides on the FRunnable Class and managed to start and destroy a new thread just fine. (see http://deliciouscookieproduction.com/multi-threading/ or 's old guide)

From my understanding, reading variables between threads is thread-safe but writing variables is not. Despite this, I found that passing the pointer of the UObject I want modified into the FRunnable object seems to work. Here is a really stripped-down version.

.h

class FMultiThreadTest : public FRunnable
{
...
static FMultiThreadTest* Runnable;
USomeUObject* InternalSomeObjectPtr;
...
}

.c

FMultiThreadTest* FMultiThreadTest::StartInitialize(USomeUObject* PassedPtr)
{
	if (!Runnable && FPlatformProcess::SupportsMultithreading())
	{
		Runnable = new FMultiThreadTest();
		Runnable->InternalSomeObjectPtr= PassedPtr;
	}
	return Runnable;
}

The thread is created by calling this from an AActor placed in the scene:

	FMultiThreadTest* ThreadRef;
	ThreadRef = FMultiThreadTest::StartInitialize(PassedPtr);

After testing, this seems to work; I can edit the values in InternalSomeObjectPtr in FMultiThreadTest::Run() without a crash even when the game thread is cycling through the values.

Is this working as intended? Do I need to use an FCriticalSection to lock editing the values in the passed pointer?

Thanks!

UObject pointer is harmless, it only contains memory address (pointer is practically integer value with address) which will be valid as long as object exists. Only issue here is reflection system not seeing the refrence of that object which means if this is only reference the object is subject to be deleted by GC, with actor this is a lot easier as actor is always refrenced by UWorld, still if actor will be destroyed you will have invalid pointer which you need to null. Simply this variable in not managed and you need to manage it yourself. The article state that you can not modify data (aspecially reflected UPROPERTY data) in UObject as well as call some built-in function in it because UObject system is simply not prepared for other threads to visit it, but holding address to UObject it self is harmless.

The main problem with multithreading is fact that threads are not guaranty to run by same CPU (since in logical sense individual CPU core is separate CPU for code), so there is no guarranty of any order of execution and all CPU execute code and not look on each other and they can confuse each other if the read and modify same variables in same time. For example you got varable X that 2 threads are using one thread check X state for some condition and later read X again, but 2nd thread modify the X in between those posits. those are situations you should avoid and this is where you should use locks so thread won’t gonan trip on eachother.

While you have this AActor you pointer you can read varables, but keep in mind that game thread will still operate your actor regardless of what your thread is doing and change those variables on the go, so reading is also risky as you may get bogus data in to your calculations. Safe is to copy, process and read down when it’s done, so you know you got data from specific time frame.

4 Likes

I can’t thank you enough for your response. It didn’t seem like passing a pointer to UObjects in a new thread would be a big deal but most of the info out there specifically said not to modify them.

Your other advice is very helpful, as well. Thank you :slight_smile: