#pragma once #include "GdCPP_Exports.h" // 不依赖 MFC 的工作线程实现 // 提供:Start/ Quit/ Wait / PostMessage / SetTimer / KillTimer / PostToThread #include #include #include #include #include #include #include #include #include #include #include #ifndef WM_QUIT_THREAD // 防止重复定义 #define WM_QUIT_THREAD (WM_USER +0) #endif class CWorkThread { public: using clock_t = std::chrono::steady_clock; using ms_t = std::chrono::milliseconds; struct Message { UINT message; WPARAM wParam; LPARAM lParam; }; CWorkThread(std::function func) : _Run(func) , m_running(false) , m_hThread(nullptr) , m_threadId(0) { } CWorkThread(int(*func)()) : _Run(std::function(func)) , m_running(false) , m_hThread(nullptr) , m_threadId(0) { } virtual ~CWorkThread() { // 尝试优雅停止(上层最好先调用 Quit() + Wait()) if (m_running) { Quit(); Wait(200); } if (m_hThread) { CloseHandle(m_hThread); m_hThread = nullptr; } unregisterThread(); } // 启动线程。返回 true 表示启动成功 bool Start(int prio = THREAD_PRIORITY_NORMAL) { if (m_running) return false; m_running = true; // 创建 native 线程,threadProc 会在内部设置线程局部 current pointer unsigned tid = 0; uintptr_t h = _beginthreadex(nullptr, 0, &CWorkThread::threadProc, this, 0, &tid); if (h == 0) { m_running = false; return false; } m_hThread = reinterpret_cast(h); m_osThreadId = tid; if (prio != THREAD_PRIORITY_NORMAL) { ::SetThreadPriority(m_hThread, prio); } // 等待线程内部注册完成(确保 PostToThread 等可用) std::unique_lock lk(m_regMutex); m_regCv.wait_for(lk, std::chrono::milliseconds(200), [this]() { return m_threadId != 0; }); return true; } // 向线程投递消息(线程安全) void PostMessage(UINT msg, WPARAM w = 0, LPARAM l = 0) { { std::lock_guard lk(m_mutex); m_queue.push(Message{ msg, w, l }); } m_cv.notify_one(); } // 等待并弹出一条消息;返回 false 表示线程正在退出 bool GetMessage(Message& outMsg) { std::unique_lock lk(m_mutex); // 主循环:只在需要时返回消息/定时器,避免递归锁定 m_mutex while (true) { // 如果线程正在退出且没有可处理的项,则返回 false if (!m_running && m_queue.empty() && timersEmpty()) { return false; } // 当既没有消息又没有定时器时,等待新的事件 if (m_queue.empty() && timersEmpty()) { m_cv.wait(lk); continue; // 重新判断条件 } auto now = clock_t::now(); // 有定时器存在 if (!m_timers.empty()) { auto it = std::min_element(m_timers.begin(), m_timers.end(), [](auto const& a, auto const& b) { return a.second.next < b.second.next; }); if (it != m_timers.end() && it->second.next <= now) { UINT_PTR timerId = it->first; // 计划下一次触发 if (it->second.period.count() > 0) { it->second.next = now + it->second.period; } else { // 单次定时器:移除 m_timers.erase(it); } outMsg = Message{ WM_TIMER, static_cast(timerId), 0 }; return true; } // 优先处理队列消息(如果有) if (!m_queue.empty()) { outMsg = m_queue.front(); m_queue.pop(); return true; } // 无消息且最近的定时器尚未到期,等待到期或有新消息到来 auto nextDueIt = std::min_element(m_timers.begin(), m_timers.end(), [](auto const& a, auto const& b) { return a.second.next < b.second.next; }); if (nextDueIt != m_timers.end()) { auto nextDue = nextDueIt->second.next; m_cv.wait_until(lk, nextDue); continue; // 重新循环,可能定时器到期或有消息 } else { // 防御性处理(理论上不应到这里) m_cv.wait(lk); continue; } } else { // 只有消息队列 if (!m_queue.empty()) { outMsg = m_queue.front(); m_queue.pop(); return true; } else { // 没有定时器但也没有消息,等待读取新消息 m_cv.wait(lk); continue; } } } } // 停止线程(发送 Quit 信号) void Quit(int /*ret*/ = 0) { // 将特殊消息插入队列以通知退出 PostMessage(WM_QUIT_THREAD, 0, 0); } // 等待线程退出,ms 毫秒;ms == INFINITE 表示无限等待 bool Wait(int ms = INFINITE) { if (!m_hThread) return true; DWORD r = ::WaitForSingleObject(m_hThread, (DWORD)ms); if (r == WAIT_OBJECT_0) { CloseHandle(m_hThread); m_hThread = nullptr; return true; } return false; } // 定时器 API:timerId 可以自定义(与原 SetTimer 行为类似) // intervalMs !=0 UINT_PTR SetTimer(UINT_PTR timerId, unsigned intervalMs) { if(intervalMs == 0) {// return 0; } std::lock_guard lk(m_mutex); // 如果timerId为0,自动生成一个唯一的timerId if(timerId == 0) { static UINT_PTR nextTimerId = 1; timerId = nextTimerId++; } m_timers[timerId] = TimerInfo{ ms_t(intervalMs), clock_t::now() + ms_t(intervalMs) }; m_cv.notify_one(); return timerId; } void KillTimer(UINT_PTR timerId) { std::lock_guard lk(m_mutex); m_timers.erase(timerId); m_cv.notify_one(); } // 将消息投递给指定的工作线程 id(线程需要已注册) static bool PostToThread(DWORD threadId, UINT msg, WPARAM w = 0, LPARAM l = 0) { std::lock_guard lk(s_registryMutex()); auto & reg = s_registry(); auto it = reg.find(threadId); if (it == reg.end()) return false; it->second->PostMessage(msg, w, l); return true; } // 返回当前 CWorkThread 实例(仅在该线程内部有效) static CWorkThread* GetCurrent() { return t_current; } // 访问线程注册 id(用于外部注册/路由) DWORD GetId() const { return m_threadId; } DWORD GetOsThreadId() const { return m_osThreadId; } protected: struct TimerInfo { ms_t period; clock_t::time_point next; }; bool timersEmpty() const { return m_timers.empty(); } // 线程入口 static unsigned __stdcall threadProc(void* pv) { CWorkThread* pThis = static_cast(pv); if (!pThis) return 0; // 标记当前线程实例(thread-local),便于 Run 中调用 GetCurrent() t_current = pThis; // 生成并注册线程 id(简单自增) pThis->m_threadId = ++s_nextThreadId(); pThis->registerThread(); // 通知 Start() 可以继续 { std::lock_guard lk(pThis->m_regMutex); pThis->m_regCv.notify_all(); } int ret = 0; try { if (pThis->_Run) ret = pThis->Run(); } catch (...) { // swallow } // 清理并注销 pThis->m_running = false; pThis->unregisterThread(); t_current = nullptr; return static_cast(ret); } // 允许子类覆写 Run(默认调用 _Run) virtual int Run() { if (_Run) return _Run(); return 0; } // 注册/注销到静态 registry,以便 PostToThread 路由 void registerThread() { std::lock_guard lk(s_registryMutex()); s_registry()[m_threadId] = this; } void unregisterThread() { std::lock_guard lk(s_registryMutex()); auto & reg = s_registry(); if (m_threadId && reg.find(m_threadId) != reg.end()) { reg.erase(m_threadId); } } private: std::function _Run; std::atomic m_running; // 消息队列 std::queue m_queue; std::mutex m_mutex; std::condition_variable m_cv; // 定时器 std::unordered_map m_timers; // native handle / ids HANDLE m_hThread; DWORD m_osThreadId = 0; DWORD m_threadId; // synthetic id for registry // 注册等待 std::mutex m_regMutex; std::condition_variable m_regCv; // 静态 registry static std::unordered_map& s_registry() { static std::unordered_map reg; return reg; } static std::mutex& s_registryMutex() { static std::mutex m; return m; } static DWORD& s_nextThreadId() { static DWORD id = 0; return id; } void registerThreadNoLock() { s_registry()[m_threadId] = this; } // thread local current pointer static inline thread_local CWorkThread* t_current = nullptr; };