UObjects can be multithreaded in a limited way. They can be constructed on worker threads and StaticFindObject (and all FindObject helper functions) can be called on any thread. In all cases any interaction with UObjects outside of the Game (main) Thread should be encapsulated in FGCScopeGuard scope:

UPackage* Package = nullptr;
{
	SCOPED_LOADTIMER(CreateLinker_CreatePackage);
	**FGCScopeGuard GCGuard;**
	**Package = CreatePackage(*Desc.Name.ToString());**
	…
}

FGCScopeGuard ensures that within its scope Garbage Collection is not running. It is important because in scenarios when a new object is constructed or an object is returned from StaticFindObject call which is not otherwise reachable it can be destroyed before it can be made referenced by a reachable object. It is also dangerous to change UObject state while GC is running (setting flags that may affect GC).

Since FGCScopeGuard prevents GC from running it’s important to keep these scopes as small and fast to execute as possible since they may affect performance of the Game Thread if GC attempts to run at the same time. Note that if Garbage Collector attempts to run while another thread is in FGCScopeGuard scope, GC will be skipped until the next frame. This only applies to incremental GC during gameplay (non incremental GC will just block the Game Thread) and GC can be skipped up to 10 times. If GC is unsuccessful after 10 attempts it will block the Game Thread until it can be executed. If worker threads need to lock FGCScopeGuard for a significant amount of time they can periodically check if GC wants to run by calling IsGarbageCollectionWaiting() function and if it returns true they can release the lock and allow GC to run. AsyncLoading.cpp code is a good example of how this is achieved.