diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 59bd3d2a66..87712a3ceb 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -202,6 +202,8 @@ add_library(core STATIC
     hle/kernel/server_port.h
     hle/kernel/server_session.cpp
     hle/kernel/server_session.h
+    hle/kernel/service_thread.cpp
+    hle/kernel/service_thread.h
     hle/kernel/session.cpp
     hle/kernel/session.h
     hle/kernel/shared_memory.cpp
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 04cae3a433..1bf4c33557 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -329,7 +329,7 @@ struct KernelCore::Impl {
     std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
 
     // Number of host threads is a relatively high number to avoid overflowing
-    static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
+    static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 1024;
     std::atomic<size_t> num_host_threads{0};
     std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
         register_host_thread_keys{};
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index a35c8aa4be..079c3911a7 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -32,12 +32,9 @@ ResultVal<std::shared_ptr<ServerSession>> ServerSession::Create(KernelCore& kern
                                                                 std::string name) {
     std::shared_ptr<ServerSession> session{std::make_shared<ServerSession>(kernel)};
 
-    session->request_event =
-        Core::Timing::CreateEvent(name, [session](std::uintptr_t, std::chrono::nanoseconds) {
-            session->CompleteSyncRequest();
-        });
     session->name = std::move(name);
     session->parent = std::move(parent);
+    session->service_thread = std::make_unique<ServiceThread>(kernel);
 
     return MakeResult(std::move(session));
 }
@@ -142,16 +139,12 @@ ResultCode ServerSession::QueueSyncRequest(std::shared_ptr<Thread> thread,
         std::make_shared<HLERequestContext>(kernel, memory, SharedFrom(this), std::move(thread));
 
     context->PopulateFromIncomingCommandBuffer(kernel.CurrentProcess()->GetHandleTable(), cmd_buf);
-    request_queue.Push(std::move(context));
+    service_thread->QueueSyncRequest(*this, std::move(context));
 
     return RESULT_SUCCESS;
 }
 
-ResultCode ServerSession::CompleteSyncRequest() {
-    ASSERT(!request_queue.Empty());
-
-    auto& context = *request_queue.Front();
-
+ResultCode ServerSession::CompleteSyncRequest(HLERequestContext& context) {
     ResultCode result = RESULT_SUCCESS;
     // If the session has been converted to a domain, handle the domain request
     if (IsDomain() && context.HasDomainMessageHeader()) {
@@ -177,18 +170,13 @@ ResultCode ServerSession::CompleteSyncRequest() {
         }
     }
 
-    request_queue.Pop();
-
     return result;
 }
 
 ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
                                             Core::Memory::Memory& memory,
                                             Core::Timing::CoreTiming& core_timing) {
-    const ResultCode result = QueueSyncRequest(std::move(thread), memory);
-    const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
-    core_timing.ScheduleEvent(delay, request_event, {});
-    return result;
+    return QueueSyncRequest(std::move(thread), memory);
 }
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index d23e9ec68f..8466b03e60 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -10,6 +10,7 @@
 #include <vector>
 
 #include "common/threadsafe_queue.h"
+#include "core/hle/kernel/service_thread.h"
 #include "core/hle/kernel/synchronization_object.h"
 #include "core/hle/result.h"
 
@@ -43,6 +44,8 @@ class Thread;
  * TLS buffer and control is transferred back to it.
  */
 class ServerSession final : public SynchronizationObject {
+    friend class ServiceThread;
+
 public:
     explicit ServerSession(KernelCore& kernel);
     ~ServerSession() override;
@@ -132,7 +135,7 @@ private:
     ResultCode QueueSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
 
     /// Completes a sync request from the emulated application.
-    ResultCode CompleteSyncRequest();
+    ResultCode CompleteSyncRequest(HLERequestContext& context);
 
     /// Handles a SyncRequest to a domain, forwarding the request to the proper object or closing an
     /// object handle.
@@ -163,11 +166,8 @@ private:
     /// The name of this session (optional)
     std::string name;
 
-    /// Core timing event used to schedule the service request at some point in the future
-    std::shared_ptr<Core::Timing::EventType> request_event;
-
-    /// Queue of scheduled service requests
-    Common::MPSCQueue<std::shared_ptr<Kernel::HLERequestContext>> request_queue;
+    /// Thread to dispatch service requests
+    std::unique_ptr<ServiceThread> service_thread;
 };
 
 } // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.cpp b/src/core/hle/kernel/service_thread.cpp
new file mode 100644
index 0000000000..59a6045df5
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.cpp
@@ -0,0 +1,100 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <thread>
+#include <vector>
+#include <queue>
+
+#include "common/assert.h"
+#include "common/scope_exit.h"
+#include "core/core.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/server_session.h"
+#include "core/hle/kernel/service_thread.h"
+#include "core/hle/lock.h"
+#include "video_core/renderer_base.h"
+
+namespace Kernel {
+
+class ServiceThread::Impl final {
+public:
+    explicit Impl(KernelCore& kernel);
+    ~Impl();
+
+    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+
+private:
+    std::vector<std::thread> threads;
+    std::queue<std::function<void()>> requests;
+    std::mutex queue_mutex;
+    std::condition_variable condition;
+    bool stop{};
+};
+
+ServiceThread::Impl::Impl(KernelCore& kernel) {
+    constexpr std::size_t SizeOfPool{1};
+    for (std::size_t i = 0; i < SizeOfPool; ++i)
+        threads.emplace_back([&] {
+            // Wait for first request before trying to acquire a render context
+            {
+                std::unique_lock lock{queue_mutex};
+                condition.wait(lock, [this] { return stop || !requests.empty(); });
+            }
+
+            kernel.RegisterHostThread();
+
+            while (true) {
+                std::function<void()> task;
+
+                {
+                    std::unique_lock lock{queue_mutex};
+                    condition.wait(lock, [this] { return stop || !requests.empty(); });
+                    if (stop && requests.empty()) {
+                        return;
+                    }
+                    task = std::move(requests.front());
+                    requests.pop();
+                }
+
+                task();
+            }
+        });
+}
+
+void ServiceThread::Impl::QueueSyncRequest(ServerSession& session,
+                                           std::shared_ptr<HLERequestContext>&& context) {
+    {
+        std::unique_lock lock{queue_mutex};
+        requests.emplace([session{SharedFrom(&session)}, context{std::move(context)}]() {
+            session->CompleteSyncRequest(*context);
+            return;
+        });
+    }
+    condition.notify_one();
+}
+
+ServiceThread::Impl::~Impl() {
+    {
+        std::unique_lock lock{queue_mutex};
+        stop = true;
+    }
+    condition.notify_all();
+    for (std::thread& thread : threads) {
+        thread.join();
+    }
+}
+
+ServiceThread::ServiceThread(KernelCore& kernel) : impl{std::make_unique<Impl>(kernel)} {}
+
+ServiceThread::~ServiceThread() = default;
+
+void ServiceThread::QueueSyncRequest(ServerSession& session,
+                                     std::shared_ptr<HLERequestContext>&& context) {
+    impl->QueueSyncRequest(session, std::move(context));
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/service_thread.h b/src/core/hle/kernel/service_thread.h
new file mode 100644
index 0000000000..d252490bba
--- /dev/null
+++ b/src/core/hle/kernel/service_thread.h
@@ -0,0 +1,27 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+
+namespace Kernel {
+
+class HLERequestContext;
+class KernelCore;
+class ServerSession;
+
+class ServiceThread final {
+public:
+    explicit ServiceThread(KernelCore& kernel);
+    ~ServiceThread();
+
+    void QueueSyncRequest(ServerSession& session, std::shared_ptr<HLERequestContext>&& context);
+
+private:
+    class Impl;
+    std::unique_ptr<Impl> impl;
+};
+
+} // namespace Kernel