Since there are no answers to this question yet I’ve decided to write a TestActor which tests if BlueprintNativeEvents (or in general blueprint events) act as threads or not.
My findings are very strange…
Here is the header for the actor:
And the implementation:
The basic idea is to see if the call to NativeFooWithoutReturn() method blocks the execution at beginPlay (and hence also the game). I calculated the time difference to see how long this block takes place.
In NativeFooWithoutReturn_Implementation() I’ve tried several things…
-
I’ve put a very long for loop (like 999999999). And it blocks the game and the editor entirely for a short time so it seems NativeFooWithoutReturn runs on the main execution thread not a separate thread once called in BeginPlay.
-
I’ve replaced the loop via FProcess::Sleep()… the same naturally occured.
In the second trial I’ve tried to implement (override) the NativeFooWithoutReturn in BlueprintEditor. I’ve derived a new class from TestActor and implemented the NativeFooWithoutReturn like this:
Surprisingly this case also blocked the game and the editor. So that means the NativeFooWithoutReturn event is not a separate thread but still running on the main execution thread.
I want to make an off topic note here as well. The BeginPlay() method on C++ side reported around 2 seconds of delay in this case. However the same code for the C++ only implementation of the NativeFooWithoutReturn function is handled in less than 0.01 seconds. That means around 200 times slow down for the editor compared to pure C++ implementation (one more reason to go for C++).
Now here comes the case which will blow your mind off!
I’ve added a latent delay node to the Editor implementation. And Kaboom! suddenly NativeFooWithoutReturn() becomes a thread. The C++ BeginPlay() reports 0 second delay and the game does not freeze. Also the ordering of the log messages change in this case as BeginPlay() on C++ prints both the messages and exits right away…
So the result of this trial is that Blueprint Events are not separate threads. They do execute on the main execution thread unless you put some latent nodes (with clock sign on top-right corner) to it (like Delay, Camera Fade methods etc.)
As a side note I’ve also tried to declare a delegate (tried multi-cast, single-cast, event etc) called FooEvent and bound a FooFunction to it in BeginPlay(). Then I called fooEvent.Broadcast() in place of NativeFooWithoutReturn(). The FooFunction has the exact same implementation as NativeFooWithoutReturn_Implementation()…
The result is the same as above and the delegate function call also blocked the game and the editor together. That means even if you use function delegates all execution is still handled within a single master execution thread by Unreal Engine.
So it seems the only way to solve my original problem is to implement a thread mechanism by myself or to use timers etc.
Therefore I answered my own question.
But I would still appreciate any comments or insights about this issue…