diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp
index 6a74cabd87..7667836957 100644
--- a/Source/Android/jni/MainAndroid.cpp
+++ b/Source/Android/jni/MainAndroid.cpp
@@ -408,12 +408,12 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_SetProfiling
                                                                                  jboolean enable)
 {
   HostThreadLock guard;
-  Core::SetState(Core::State::Paused);
-  auto& jit_interface = Core::System::GetInstance().GetJitInterface();
-  jit_interface.ClearCache();
+  auto& system = Core::System::GetInstance();
+  auto& jit_interface = system.GetJitInterface();
+  const Core::CPUThreadGuard cpu_guard(system);
+  jit_interface.ClearCache(cpu_guard);
   jit_interface.SetProfilingState(enable ? JitInterface::ProfilingState::Enabled :
                                            JitInterface::ProfilingState::Disabled);
-  Core::SetState(Core::State::Running);
 }
 
 JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_WriteProfileResults(JNIEnv*,
diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp
index c7c392fa0e..a7f743d9ee 100644
--- a/Source/Core/Core/Core.cpp
+++ b/Source/Core/Core/Core.cpp
@@ -1015,18 +1015,17 @@ void UpdateWantDeterminism(bool initial)
   {
     NOTICE_LOG_FMT(COMMON, "Want determinism <- {}", new_want_determinism ? "true" : "false");
 
-    RunAsCPUThread([&] {
-      s_wants_determinism = new_want_determinism;
-      const auto ios = system.GetIOS();
-      if (ios)
-        ios->UpdateWantDeterminism(new_want_determinism);
+    const Core::CPUThreadGuard guard(system);
+    s_wants_determinism = new_want_determinism;
+    const auto ios = system.GetIOS();
+    if (ios)
+      ios->UpdateWantDeterminism(new_want_determinism);
 
-      system.GetFifo().UpdateWantDeterminism(new_want_determinism);
+    system.GetFifo().UpdateWantDeterminism(new_want_determinism);
 
-      // We need to clear the cache because some parts of the JIT depend on want_determinism,
-      // e.g. use of FMA.
-      system.GetJitInterface().ClearCache();
-    });
+    // We need to clear the cache because some parts of the JIT depend on want_determinism,
+    // e.g. use of FMA.
+    system.GetJitInterface().ClearCache(guard);
   }
 }
 
diff --git a/Source/Core/Core/PowerPC/BreakPoints.cpp b/Source/Core/Core/PowerPC/BreakPoints.cpp
index 2d62dd569c..4aeb376643 100644
--- a/Source/Core/Core/PowerPC/BreakPoints.cpp
+++ b/Source/Core/Core/PowerPC/BreakPoints.cpp
@@ -271,30 +271,30 @@ void MemChecks::AddFromStrings(const TMemChecksStr& mc_strings)
 void MemChecks::Add(TMemCheck memory_check)
 {
   bool had_any = HasAny();
-  Core::RunAsCPUThread([&] {
-    // Check for existing breakpoint, and overwrite with new info.
-    // This is assuming we usually want the new breakpoint over an old one.
-    const u32 address = memory_check.start_address;
-    auto old_mem_check =
-        std::find_if(m_mem_checks.begin(), m_mem_checks.end(),
-                     [address](const auto& check) { return check.start_address == address; });
-    if (old_mem_check != m_mem_checks.end())
-    {
-      const bool is_enabled = old_mem_check->is_enabled;  // Preserve enabled status
-      *old_mem_check = std::move(memory_check);
-      old_mem_check->is_enabled = is_enabled;
-      old_mem_check->num_hits = 0;
-    }
-    else
-    {
-      m_mem_checks.emplace_back(std::move(memory_check));
-    }
-    // If this is the first one, clear the JIT cache so it can switch to
-    // watchpoint-compatible code.
-    if (!had_any)
-      m_system.GetJitInterface().ClearCache();
-    m_system.GetMMU().DBATUpdated();
-  });
+
+  const Core::CPUThreadGuard guard(m_system);
+  // Check for existing breakpoint, and overwrite with new info.
+  // This is assuming we usually want the new breakpoint over an old one.
+  const u32 address = memory_check.start_address;
+  auto old_mem_check =
+      std::find_if(m_mem_checks.begin(), m_mem_checks.end(),
+                   [address](const auto& check) { return check.start_address == address; });
+  if (old_mem_check != m_mem_checks.end())
+  {
+    const bool is_enabled = old_mem_check->is_enabled;  // Preserve enabled status
+    *old_mem_check = std::move(memory_check);
+    old_mem_check->is_enabled = is_enabled;
+    old_mem_check->num_hits = 0;
+  }
+  else
+  {
+    m_mem_checks.emplace_back(std::move(memory_check));
+  }
+  // If this is the first one, clear the JIT cache so it can switch to
+  // watchpoint-compatible code.
+  if (!had_any)
+    m_system.GetJitInterface().ClearCache(guard);
+  m_system.GetMMU().DBATUpdated();
 }
 
 bool MemChecks::ToggleBreakPoint(u32 address)
@@ -318,21 +318,19 @@ void MemChecks::Remove(u32 address)
   if (iter == m_mem_checks.cend())
     return;
 
-  Core::RunAsCPUThread([&] {
-    m_mem_checks.erase(iter);
-    if (!HasAny())
-      m_system.GetJitInterface().ClearCache();
-    m_system.GetMMU().DBATUpdated();
-  });
+  const Core::CPUThreadGuard guard(m_system);
+  m_mem_checks.erase(iter);
+  if (!HasAny())
+    m_system.GetJitInterface().ClearCache(guard);
+  m_system.GetMMU().DBATUpdated();
 }
 
 void MemChecks::Clear()
 {
-  Core::RunAsCPUThread([&] {
-    m_mem_checks.clear();
-    m_system.GetJitInterface().ClearCache();
-    m_system.GetMMU().DBATUpdated();
-  });
+  const Core::CPUThreadGuard guard(m_system);
+  m_mem_checks.clear();
+  m_system.GetJitInterface().ClearCache(guard);
+  m_system.GetMMU().DBATUpdated();
 }
 
 TMemCheck* MemChecks::GetMemCheck(u32 address, size_t size)
diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp
index ed60b4d489..fb1b4f3a95 100644
--- a/Source/Core/Core/PowerPC/JitInterface.cpp
+++ b/Source/Core/Core/PowerPC/JitInterface.cpp
@@ -241,7 +241,7 @@ bool JitInterface::HandleStackFault()
   return m_jit->HandleStackFault();
 }
 
-void JitInterface::ClearCache()
+void JitInterface::ClearCache(const Core::CPUThreadGuard&)
 {
   if (m_jit)
     m_jit->ClearCache();
diff --git a/Source/Core/Core/PowerPC/JitInterface.h b/Source/Core/Core/PowerPC/JitInterface.h
index f1d88b2ab0..4cf7c3179c 100644
--- a/Source/Core/Core/PowerPC/JitInterface.h
+++ b/Source/Core/Core/PowerPC/JitInterface.h
@@ -16,8 +16,9 @@ class JitBase;
 
 namespace Core
 {
+class CPUThreadGuard;
 class System;
-}
+}  // namespace Core
 namespace PowerPC
 {
 enum class CPUCore;
@@ -72,7 +73,7 @@ public:
   bool HandleStackFault();
 
   // Clearing CodeCache
-  void ClearCache();
+  void ClearCache(const Core::CPUThreadGuard& guard);
 
   // This clear is "safe" in the sense that it's okay to run from
   // inside a JIT'ed block: it clears the instruction cache, but not
diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp
index 07d4d21cbb..cb79feff94 100644
--- a/Source/Core/DolphinQt/MenuBar.cpp
+++ b/Source/Core/DolphinQt/MenuBar.cpp
@@ -1753,7 +1753,8 @@ void MenuBar::PatchHLEFunctions()
 
 void MenuBar::ClearCache()
 {
-  Core::RunAsCPUThread([] { Core::System::GetInstance().GetJitInterface().ClearCache(); });
+  auto& system = Core::System::GetInstance();
+  system.GetJitInterface().ClearCache(Core::CPUThreadGuard{system});
 }
 
 void MenuBar::LogInstructions()