Why does my gamethread get stuck when running the second thread?

I’m having a problem when using multithreading in UE4. I’m designing a module in a VR project that receives data from VR devices to calculate the player’s position in the real world to correct the player’s position in the game. Since this operation is very computationally intensive (needs 2-3 sec on my PC), I opened a new thread (FRunnableThread) dedicated to it. See the second thread part in the following image:

However, during the test, my main game thread would get stuck until the second thread finished calculating. With the log file, I can confirm that I did open the second thread.
image

Is this because the main thread of the game is waiting to sync with the second thread? If I want the player to still be able to move around the game while the second thread is doing the calculations, how can I do that?

// head file
UCLASS()
class OPENCV_API ALoadDoorCornerCoords : public AActor
{
	GENERATED_BODY()
		UFUNCTION(BlueprintCallable, Category = LoadCSV, meta = (Keywords = "Load Door Corners", NativeBreakFunc))
		TArray<FVector2D> LoadCorners();

public:
	// Sets default values for this actor's properties
	ALoadDoorCornerCoords();

protected:
	// Called when the game starts or when spawned  
	virtual void BeginPlay() override;
public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;
};

class FLoadCorners :public FRunnable
{
public:

	FLoadCorners();
	~FLoadCorners();
	TArray<FVector2D> corners;
	class FRunnableThread* MyLoadCornersThread;
	static FLoadCorners* MyLoadCorners;
	static void JoyInit();
	static void Shutdown();
	FCriticalSection m_mutex;
	static int runCount1;

private:
	virtual bool Init() override;
	virtual uint32 Run() override;
	virtual void Stop() override;
	virtual void Exit() override;
};

LoadCornersProcess() in the cpp file is the function used to get the player’s position.

// cpp file
FLoadCorners* FLoadCorners::MyLoadCorners = nullptr;

FLoadCorners::FLoadCorners()
{
    UE_LOG(LogTemp, Warning, TEXT("MyLoadCorners Add Instance"));
    MyLoadCornersThread = FRunnableThread::Create(this, TEXT("MyLoadCornersRunnableThread"));
    if (MyLoadCorners != nullptr) {
        delete MyLoadCorners;
        MyLoadCorners = nullptr;
    }
}

FLoadCorners::~FLoadCorners()
{
    delete MyLoadCornersThread;
    MyLoadCornersThread = nullptr;
    UE_LOG(LogTemp, Warning, TEXT("MyLoadCorners Del Instance"));
}

bool FLoadCorners::Init() {
    UE_LOG(LogTemp, Warning, TEXT("MyLoadCorners Init"));
    return true;
}

void FLoadCorners::JoyInit()
{
    if (FPlatformProcess::SupportsMultithreading())
    {
        MyLoadCorners = new FLoadCorners();
    }
}

uint32 FLoadCorners::Run()
{
    UE_LOG(LogTemp, Warning, TEXT("MyLoadCorners Run"));    
    UE_LOG(LogTemp, Warning, TEXT("Current Thread ID: %s"), *FString::FromInt(FPlatformTLS::GetCurrentThreadId()));
    FLoadCorners::corners = TArray<FVector2D>();
    FLoadCorners::corners = LoadCornersProcess();

    FString arrayLens = FString::FromInt(FLoadCorners::corners.Num());
    UE_LOG(LogTemp, Warning, TEXT("MyLoadCorners Result Lenth: %s"), *arrayLens);
    return 0;
}

void FLoadCorners::Stop()
{
    MyLoadCornersThread->WaitForCompletion();
    UE_LOG(LogTemp, Warning, TEXT("MyLoadCorners Stop"));
}

void FLoadCorners::Exit() {
    UE_LOG(LogTemp, Warning, TEXT("MyFindVertices Exit"));
}

void FLoadCorners::Shutdown()
{
    MyLoadCorners->Stop();
    MyLoadCorners->~FLoadCorners();
}

// Sets default values
ALoadDoorCornerCoords::ALoadDoorCornerCoords()
{
    // Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
    PrimaryActorTick.bCanEverTick = false;
}

// Called when the game starts or when spawned
void ALoadDoorCornerCoords::BeginPlay()
{
    Super::BeginPlay();
}

// Called every frame
void ALoadDoorCornerCoords::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);
}

int FLoadCorners::runCount1 = 0;
TArray<FVector2D> ALoadDoorCornerCoords::LoadCorners()
{
    UE_LOG(LogTemp, Warning, TEXT("Current Thread ID: %s"), *FString::FromInt(FPlatformTLS::GetCurrentThreadId()));
    FLoadCorners::JoyInit();
    FLoadCorners::Shutdown();
    FLoadCorners::runCount1++;
    UE_LOG(LogTemp, Warning, TEXT("Current Thread ID: %s"), *FString::FromInt(FPlatformTLS::GetCurrentThreadId()));
    return FLoadCorners::MyLoadCorners->corners;
}

The blueprint system, and unreal GUI, must run on the main thread.

The way to structure an asynchronous task that lets the main thread continue, is to register some kind of event with that task/thread, and then have that task/thread invoke it on the main thread when it’s done.

Beware that, if the callback task is in some object that can get unloaded, you may be in for a hard to debug crash. Put the callback in something permanent, like the GameInstance. (GameMode is only available on the server; if your game is single player, that might work, too.)

You can invoke the completion callback/delegate using an async task created from your other thread, like so:

AsyncTask(ENamedThreads::GameThread, &]()
{ 
    // Code placed here will run in the game thread 
});