diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp
index 3cb995ddbb..7a5e6fc08f 100644
--- a/src/core/hle/kernel/k_thread.cpp
+++ b/src/core/hle/kernel/k_thread.cpp
@@ -30,6 +30,7 @@
 #include "core/hle/kernel/k_system_control.h"
 #include "core/hle/kernel/k_thread.h"
 #include "core/hle/kernel/k_thread_queue.h"
+#include "core/hle/kernel/k_worker_task_manager.h"
 #include "core/hle/kernel/kernel.h"
 #include "core/hle/kernel/svc_results.h"
 #include "core/hle/kernel/time_manager.h"
@@ -332,7 +333,7 @@ void KThread::Finalize() {
     }
 
     // Perform inherited finalization.
-    KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>::Finalize();
+    KSynchronizationObject::Finalize();
 }
 
 bool KThread::IsSignaled() const {
@@ -376,11 +377,28 @@ void KThread::StartTermination() {
 
     // Register terminated dpc flag.
     RegisterDpc(DpcFlag::Terminated);
+}
+
+void KThread::FinishTermination() {
+    // Ensure that the thread is not executing on any core.
+    if (parent != nullptr) {
+        for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
+            KThread* core_thread{};
+            do {
+                core_thread = kernel.Scheduler(i).GetCurrentThread();
+            } while (core_thread == this);
+        }
+    }
 
     // Close the thread.
     this->Close();
 }
 
+void KThread::DoWorkerTaskImpl() {
+    // Finish the termination that was begun by Exit().
+    this->FinishTermination();
+}
+
 void KThread::Pin(s32 current_core) {
     ASSERT(kernel.GlobalSchedulerContext().IsLocked());
 
@@ -1027,6 +1045,9 @@ void KThread::Exit() {
 
         // Start termination.
         StartTermination();
+
+        // Register the thread as a work task.
+        KWorkerTaskManager::AddTask(kernel, KWorkerTaskManager::WorkerType::Exit, this);
     }
 }
 
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h
index 92c3493c5d..cc427f6cfc 100644
--- a/src/core/hle/kernel/k_thread.h
+++ b/src/core/hle/kernel/k_thread.h
@@ -19,6 +19,7 @@
 #include "core/hle/kernel/k_light_lock.h"
 #include "core/hle/kernel/k_spin_lock.h"
 #include "core/hle/kernel/k_synchronization_object.h"
+#include "core/hle/kernel/k_worker_task.h"
 #include "core/hle/kernel/slab_helpers.h"
 #include "core/hle/kernel/svc_common.h"
 #include "core/hle/kernel/svc_types.h"
@@ -100,7 +101,7 @@ enum class ThreadWaitReasonForDebugging : u32 {
 [[nodiscard]] KThread& GetCurrentThread(KernelCore& kernel);
 [[nodiscard]] s32 GetCurrentCoreId(KernelCore& kernel);
 
-class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KSynchronizationObject>,
+class KThread final : public KAutoObjectWithSlabHeapAndContainer<KThread, KWorkerTask>,
                       public boost::intrusive::list_base_hook<> {
     KERNEL_AUTOOBJECT_TRAITS(KThread, KSynchronizationObject);
 
@@ -385,6 +386,8 @@ public:
 
     void OnTimer();
 
+    void DoWorkerTaskImpl();
+
     static void PostDestroy(uintptr_t arg);
 
     [[nodiscard]] static ResultCode InitializeDummyThread(KThread* thread);
@@ -679,6 +682,8 @@ private:
 
     void StartTermination();
 
+    void FinishTermination();
+
     [[nodiscard]] ResultCode Initialize(KThreadFunction func, uintptr_t arg, VAddr user_stack_top,
                                         s32 prio, s32 virt_core, KProcess* owner, ThreadType type);