Multi-threading in Blueprints

Is it currently possible to set up multi-threading using blueprints alone? Long story short, I would like to shift my auto-saving system on to its own thread to prevent the hitch that it currently causes. I know the amazing Rama wrote a guide explaining how to do it via code, but I’m curious if there is a blueprint alternative at the moment, or if there are plans for one in the near future.

Thanks in advance :slight_smile:

To make a task run in a semi-async fashion, call Delay 0, this will cause the rest of that pin to run on the next frame. This is not a new thread.

To get real threading, you’ll have to implement it in C++, or try plugins that have implemented it like:

Interesting. So simply adding a Delay node but with a time of “0” causes what follows it to run async? I’ll have to give it a try! Thanks for the help, much appreciated :slight_smile:

Easy preudo multithreading

Interesting - thanks for the reply :slight_smile: I’m not quite sure how or why this would make any difference, but you’ve definitely given me another avenue to research! And at the perfect time, too - I’m planning to have another stab at tackling this problem again soon.

At the moment, I’m not seeing how this would be any different from using a Sequence to fire a series of custom events, but I assume it has something to do with timelines having some in-built feature that automatically handles multithreading?

With this method, I’m guessing I’d just need to chop all the processes into smaller batches, so they individually don’t cause a hitch, then stagger them as necessary? Meh, I still don’t understand how that’s any different from firing staggered custom events, but I’ll do some more research!

Thanks again :slight_smile:

That is not multitrheading! It is not called sequence out of boredom… it is sequential ( one after another …)

Yes it’s not multi-threading but it’s parallel. After the first branch starts, the sequence branch proceeds to execute the next sequence pin (doesn’t matter if the first one completed execution or not). That’s to say it’s a multitasking trick (context switching) takes place to give the illusion that they’re operating at the same time. For cascaded nodes, it’s a different story, you can’t start the next node until the current one finishes. So there’s no way two nodes connected in cascade structure are the same as what a sequence node does. TLDR: it’s not one after another

2 Likes

just for completion (since ive arrived here looking for answers)
rama tutorial on multithread Rama Code: Multi-Threading in UE5 C++ | Community tutorial

multithread on the wiki Multi-Threading: How to Create Threads in UE4 | Unreal Engine Community Wiki

also you can have a blueprint function library with a function that calls Async or AsyncTask and receives 2 delegates. executes 1 for inside the async task, and another one when it finishes (probably wrapped in another AysncTask for the game thread).
you can then pass some events to those delegates on bp.

I`m a bit confused why they dont make the sequence node spawn threads according to some simple logic ?

because that’s not what it is for.
you could make your own nodes that serve a similar function.

Multithreading is not a goal, it’s a process.
Not what it is for does not apply.
I will definitely have a look at making a node that does this once I get comfy with the libraries.

1 Like

i just replied to that question. why dont they make it do something else? because that’s not what that node is for. if you want that functionality you should implement something else.

for example:

this code is lgpl3-only copyright me


// Copyright (C) 2023 - Jeronimo Barraco-Marmol. All rights reserved.
// SPDX-License-Identifier: LGPL-3.0-only

#pragma once
 // excerpt of my own library

#include "CoreMinimal.h"

class UWorld;
class UWidget;

#include "JMiscUtils.generated.h"

DECLARE_DYNAMIC_DELEGATE(FOnJAsync);
DECLARE_DYNAMIC_DELEGATE(FOnJAsyncDone);

// Type of graph to use
UENUM(BlueprintType)
enum class EAsyncExec: uint8
	// Engine\\Source\\Runtime\Core\\Public\Async\Async.h:27
	// the type used on AsyncTask is much more granular. but unfortunately it´s not BlueprintType and is int32 so it cant be exposed
{
	/** Execute in Task Graph (for short running tasks). */
	TaskGraph,

	/** Execute in Task Graph on the main thread (for short running tasks). */
	TaskGraphMainThread,

	/** Execute in separate thread if supported (for long running tasks). */
	Thread,

	/** Execute in separate thread if supported or supported post fork (see FForkProcessHelper::CreateThreadIfForkSafe) (for long running tasks). */
	ThreadIfForkSafe,

	/** Execute in global queued thread pool. */
	ThreadPool,
};

UCLASS(Blueprintable)
class JUTILS_API UJMiscUtils: public UBlueprintFunctionLibrary {
	GENERATED_BODY()
public:
	
	// Calls a Task (a Delegate) on another thread, when finishes calls OnDone on the game thread (if bound)
	// What Not to Do:
	//	* Do not try to modify, create, or delete UObjects from other threads!
	//	* You can prepare all the data / do all the calculations, but only the game thread should be actually spawning / modifying / deleting UObjects / AActors.
	//	* Dont try to use TimerManager outside of the game thread :)
	//	* Don´t try to draw debug lines/points etc, as it will likely crash, ie DrawDebugLine(etc...)
	UFUNCTION(BlueprintCallable)
	static void BPASync(const FOnJAsync& Task, const FOnJAsyncDone& Done, EAsyncExec Exec = EAsyncExec::ThreadPool);
}

/// on the cpp

void UJMiscUtils::BPASync(const FOnJAsync& Task, const FOnJAsyncDone& Done, EAsyncExec Exec) {
// using [Task, Done] instead of & since it's much safer. re-entrant calls could crash otherwise.
	Async((EAsyncExecution) Exec, [Task, Done]
	{
		Task.ExecuteIfBound();
		if (Done.IsBound()) {
			AsyncTask(ENamedThreads::GameThread, [Done]
			{
				Done.ExecuteIfBound();
			});
		}
	});
}
1 Like

I think we’re talking next to each other.

THX for the example code, though !

1 Like