Announcement

Collapse
No announcement yet.

Threads & FRunnable - One actor run correct the other does not but both are the same class?

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Threads & FRunnable - One actor run correct the other does not but both are the same class?

    Hello,

    I am struggling with two actors of the same class (AMyActor) that use FRunnable to create a thread for background processing to create a mesh. When the two actors are placed in the same level, the first actor runs the thread correctly and produces a mesh but the second reports that the Runnable variable is not null and therefore does not create its new class and fails.

    What I want to acheive is store a pointer to the thread in MyActor.h and create the new thread in StartCreateMesh(). Then I can use that pointer to stop the thread using Shutdown() or let it run until complete and Shut itself down.

    CreateMeshTasks.h

    Code:
    class FCreateMeshTask : public FRunnable
    {
    //Singleton instance. Can access the thread at any time using static accessors.
    static FCreateMeshTask* Runnable;
    
    //Thread to run the worker on
    FRunnableThread* Thread;
    
    //Thread safe counter to stop the thread
    FThreadSafeCounter StopTaskCounter;
    
    public:
    
    //Constructor
    FCreateMeshTask::FCreateMeshTask(int32 x, int32 y, FMeshCallback& callback)
    : StopTaskCounter(0)
    {
    X = x;
    Y = y;
    
    Callback = callback;
    
    //Create the new thread
    Thread = FRunnableThread::Create(this, TEXT("FCreateMeshTask"), 0, TPri_AboveNormal);
    }
    
    //Destructor
    virtual ~FCreateMeshTask()
    {
    delete Thread;
    }
    
    //Implement the FRunnable interface
    virtual bool Init() override
    {
    return true;
    }
    
    virtual uint32 Run();
    
    virtual void Exit() override
    {
    FMeshTaskResult result;
    result.Columns = TArray<FTableIndices>(MeshColumns);
    result.Rows = TArray<FTableIndices>(MeshRows);
    
    AsyncTask(ENamedThreads::GameThread, [this, result]()
    {
    if (Callback.IsBound())
    Callback.Execute(result);
    });
    }
    
    virtual void Stop() override
    {
    StopTaskCounter.Increment();
    }
    
    FORCEINLINE bool ShouldStop() const
    {
    return StopTaskCounter.GetValue() > 0;
    }
    
    //Make sure this thread has stopped
    void EnsureCompletion()
    {
    Stop();
    Thread->WaitForCompletion();
    }
    
    public:
    
    static FCreateMeshTask* StartWorker(int32 x, int32 y, FMeshCallback& callback)
    {
    //This Runnable var reports not nullptr in the second actor and fails
    if (Runnable == nullptr && FPlatformProcess::SupportsMultithreading())
    {
    Runnable = new FCreateMeshTask(x, y, callback);
    }
    
    return Runnable;
    }
    
    //Shuts down the thread
    static void Shutdown()
    {
    if (Runnable)
    {
    Runnable->EnsureCompletion();
    delete Runnable;
    Runnable = nullptr;
    }
    }
    
    //Init all vars
    int32 X = 0;
    int32 Y = 0;
    FMeshCallback Callback;
    }
    
    FCreateMeshTask* FCreateMeshTask::Runnable = nullptr;
    CreateMeshTasks.cpp

    Code:
    uint32 FCreateMeshTask::Run()
    {
    //Initial wait
    FPlatformProcess::Sleep(0.05f);
    
    for (int32 x = 0; x <= X; x++)
    for (int32 y = 0; y <= Y; y++)
    {
    if (ShouldStop())
    return 0;
    
    //More code here
    }
    }
    
    return 0;
    }
    MyActor.h

    Code:
    FCreateMeshTask* CreateMeshTaskWorker;
    MyActor.cpp

    Code:
    void AMyActor::StartCreateMesh()
    {
    if (!createMeshCompletedCallback.IsBound())
    createMeshCompletedCallback = FMeshCallback::CreateUObject(this, &AMyActor::CreateMeshTaskCompleted);
    
    CreateMeshTaskWorker = FCreateMeshTask::StartWorker(X, Y, createMeshCompletedCallback);
    }
    
    void AMyActor::CreateMeshTaskCompleted(FMeshTaskResult meshResult)
    {
    //Shutdown the FCreateMeshTask thread since it's finished
    CreateMeshTaskWorker->Shutdown();
    
    //More processing
    }
    I can't understand why two separate actors that each create a thread do not seem to be able to run at the same time. One reports the Runnable variable as NULL and runs correctly while the second then report the Runnable var as not null and fails.

    I tried adding a random number to the end of the thread name using FMath::Rand() and FString::Printf which did create the name plus a number but the problem still existed.

    I could use some help, thank you
    Last edited by 3dev; 08-02-2020, 12:39 PM.

    #2
    As your code comment points it out its a singleton, you can’t create it more than once unless you’ve destroyed it in the meantime. Static variables live in class scope and singletons are anti pattern of multithreading, you should read upon static and singleton then redesign your code afterwards.

    Comment

    Working...
    X