C++:主线程异步调用

该博客详细介绍了在Windows环境下实现消息循环和任务调度的机制。通过`CMessageLoopWin`类,实现了发送任务、发布任务和延迟任务的功能,使用了`std::function`和`std::future`来处理异步任务。`MessageWndProc`窗口过程处理接收到的任务,确保任务在正确的线程上下文中执行。此外,还涉及到了线程同步和定时任务的管理。
摘要由CSDN通过智能技术生成

.h

#pragma once

#define SEND_TASK(...)				_message_loop_win.SendTask(__VA_ARGS__)
#define POST_TASK(...)				_message_loop_win.PostTask(__VA_ARGS__)
#define POST_DELAY_TASK(...)		_message_loop_win.PostDelayTask(__VA_ARGS__)
#define CURRENTLY_ON_TID_UI			_message_loop_win.CurrentlyOnTidUI()

#define POST_MAIN_THRAD_TASK(...)		POST_TASK(__VA_ARGS__)
#define POST_MAIN_THRAD_SYNC_TASK(...)	SEND_TASK(__VA_ARGS__)

#include <functional>
#include <windows.h>
#include <queue>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <future>
#include <tchar.h>
#include <memory>
#include <list>

class CMessageLoopWin
{
	using Task = std::function<void()>;
	using DelayTask = std::function<DWORD()>;

	friend LRESULT CALLBACK MessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

public:
	CMessageLoopWin();
	~CMessageLoopWin();

	void Close();

	template<class F, class... Args>
	auto SendTask(HWND hwnd, F&& f, Args&&... args)->std::future<typename std::result_of<F(Args...)>::type>
	{
		using return_type = typename std::result_of<F(Args...)>::type;
		auto task = std::make_shared<std::packaged_task<return_type()>>(std::bind(std::forward<F>(f), std::forward<Args>(args)...));
		std::future<return_type> res = task->get_future();

		PostTask(hwnd, [&]{ (*task)(); });

		res.wait();

		return res;
	}

	void PostTask(HWND hwnd, const Task& task)
	{
		std::shared_ptr<Task> safe_task = std::make_shared<Task>([=]
		{
			if (hwnd == nullptr || IsWindow(hwnd))
				task();
		});

		std::lock_guard<std::recursive_mutex> lck(_mutex);
		_task_group.push(safe_task);

		::PostMessage(_message_hwnd_, _message_id, 0, 0);
	}

	void PostDelayTask(HWND hwnd, DWORD dwDelay, const Task& task)
	{
		{
			std::lock_guard<std::mutex> _locker(_delay_mutex);

			if (_delay_thread == NULL)
			{
				_delay_thread = CreateThread(NULL, 0, _delay_thread_func, this, 0, NULL);
			}
		}

		DWORD dwBegin = GetTickCount();

		std::shared_ptr<DelayTask> delay_task = std::make_shared<DelayTask>([=]
		{
			DWORD dwTime = GetTickCount() - dwBegin;

			if (dwTime >= dwDelay)
			{
				PostTask(hwnd, task);
				return (DWORD)0;
			}
			else
			{
				return dwDelay - dwTime;
			}
		});

		{
			std::lock_guard<std::mutex> _locker(_delay_mutex);
			_delay_task_group.push_back(delay_task);
		}

		_delay_cond.notify_all();
	}

	inline bool CurrentlyOnTidUI()
	{
		return _thread_id == GetCurrentThreadId();
	}

protected:
	HWND	_message_hwnd_;
	UINT	_message_id;
	DWORD	_thread_id;

	std::recursive_mutex _mutex;
	std::queue<std::shared_ptr<Task>> _task_group;

	bool _stop;
	std::mutex _delay_mutex;
	std::list<std::shared_ptr<DelayTask>> _delay_task_group;
	std::condition_variable _delay_cond;
	HANDLE _delay_thread;

	static DWORD WINAPI _delay_thread_func(LPVOID p);
};

extern CMessageLoopWin _message_loop_win;

class CTaskVirtualWin
{
public:
	CTaskVirtualWin()
		: m_hNotifyWnd(NULL)
		, m_hVirtualWnd(NULL)
	{
		const TCHAR kVirtualWndClass[] = _T("CVirtualTaskWindow");

		WNDCLASSEX wc = { sizeof(wc) };

		HINSTANCE hInstance = ::GetModuleHandle(NULL);

		wc.lpfnWndProc		= MessageWndProc;
		wc.hInstance		= hInstance;
		wc.lpszClassName	= kVirtualWndClass;
		RegisterClassEx(&wc);

		m_hVirtualWnd = CreateWindow(kVirtualWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0);

		::SetWindowLongPtr(m_hVirtualWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
	}

	virtual ~CTaskVirtualWin()
	{
		::SetWindowLongPtr(m_hVirtualWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(nullptr));

		SendMessage(m_hVirtualWnd, WM_CLOSE, 0, 0);
	}

	inline HWND GetVirtualWnd() const 
	{ 
		return m_hVirtualWnd; 
	}

	void SetNotifyWnd(HWND _hwnd)
	{
		m_hNotifyWnd = _hwnd;
	}
		
	static LRESULT CALLBACK MessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
	{
		CTaskVirtualWin* self = (CTaskVirtualWin*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);

		if (self != NULL && self->m_hNotifyWnd != NULL)
		{
			return ::PostMessage(self->m_hNotifyWnd, message, wParam, lParam);
		}
		else
		{
			return DefWindowProc(hWnd, message, wParam, lParam);
		}
	}
protected:
	HWND m_hVirtualWnd;
	HWND m_hNotifyWnd;
};

.cpp

#include "stdafx.h"
#include "main_message_task.h"
#include <tchar.h>

const TCHAR kWndClass[]			= _T("CMessageWindow");
const TCHAR kTaskMessageName[]	= _T("CCustomTask");

CMessageLoopWin _message_loop_win;

CMessageLoopWin::CMessageLoopWin()
: _message_hwnd_(nullptr)
, _message_id(::RegisterWindowMessage(kTaskMessageName))
, _thread_id(GetCurrentThreadId())
, _stop(false)
, _delay_thread(NULL)
{
	WNDCLASSEX wc = { sizeof(wc) };
	HINSTANCE hInstance = ::GetModuleHandle(NULL);
	wc.lpfnWndProc = MessageWndProc;
	wc.hInstance = hInstance;
	wc.lpszClassName = kWndClass;
	RegisterClassEx(&wc);
	_message_hwnd_ = CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hInstance, 0);

	::SetWindowLongPtr(_message_hwnd_, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(this));
}

CMessageLoopWin::~CMessageLoopWin()
{
	Close();
}

void CMessageLoopWin::Close()
{
	if (!_stop)
	{
		_stop = true;
		_delay_cond.notify_all();
		WaitForSingleObject(_delay_thread, INFINITE);
		_delay_task_group.clear();
	}
}

DWORD WINAPI CMessageLoopWin::_delay_thread_func(LPVOID p)
{
	CMessageLoopWin* pWin = (CMessageLoopWin*)p;

	while (!pWin->_stop)
	{
		DWORD dwWaitTime = INFINITE;

		{
			std::lock_guard<std::mutex> _locker(pWin->_delay_mutex);
			for (auto it = pWin->_delay_task_group.begin(); it != pWin->_delay_task_group.end();)
			{
				DWORD dwRet = (*(*it))();

				if (dwRet == 0)
				{
					pWin->_delay_task_group.erase(it++);
					continue;
				}
				else if (dwRet < dwWaitTime)
				{
					dwWaitTime = dwRet;
				}

				++it;
			}
		}

		pWin->_delay_cond.wait_for(std::unique_lock<std::mutex>(pWin->_delay_mutex), std::chrono::milliseconds(dwWaitTime));
	}

	return 0;
}

LRESULT CALLBACK MessageWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	CMessageLoopWin* self = (CMessageLoopWin*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);

	if (self && message == self->_message_id) 
	{	
		std::shared_ptr<CMessageLoopWin::Task> task;

		{
			std::lock_guard<std::recursive_mutex> lck(self->_mutex);
			if (!self->_task_group.empty())
			{
				task = std::move(self->_task_group.front());
				self->_task_group.pop();
			}
		}

		if (task != nullptr)
		{
			(*task)();
		}
	}
	else switch (message) {
	case WM_DESTROY:
		// Clear the reference to |self|.
		LONG_PTR result = ::SetWindowLongPtr(hWnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(nullptr));
		break;
	}

	return DefWindowProc(hWnd, message, wParam, lParam);
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值