我有一个正在轮询服务器的后台线程。当有数据时,我想在 UI 线程上处理数据。如果我存储主窗口的hwnd
。
如何让特定方法 static void DataHandler(void* data)
在 UI 线程上执行?
我认为创建一个传递 hwnd
和函数指针的计时器会起作用。但是有更好的方法吗?我可以使用 PostMessage
以某种方式调用数据处理程序吗?
此外,我没有编写 UI 代码,因此我无法修改消息循环中的任何内容。
最佳答案
我最常使用两种主要方法在线程之间进行通信。
1) PostMessage()
创建自定义 Windows 消息,ala:
#define WM_YOU_HANVE_DATA WM_USER + 101
创建一个自定义数据类型,用于保存要发送到主线程进行处理的数据:
struct MyData
{
string client_;
string message_type_;
string payload_;
};
在工作线程中,在堆上实例化 MyData
的拷贝,填充它,然后将它发送到主线程:
MyData* data = new MyData;
data->client_ = "hoser";
// ... etc
PostMessage(main_wnd_handle, WM_YOU_HAVE_DATA, reinterpret_cast<WPARAM>(data), );
在主线程中,以任何合适的方式处理这个消息和数据。
BEGIN_MESSAGE_MAP(MyAppWindow, CDialogEx)
// ... stuff's going to already be here
ON_MESSAGE(WM_YOU_HAVE_DATA, OnYouHaveData)
END_MESSAGE_MAP()
// ...
重要说明:MyAppWindow
的主线程现在拥有 MyData*
指向的内存,因此您必须取得所有权它的。我在此处使用 auto_ptr
执行此操作:
LRESULT MyAppWindow::OnYouHaveData(WPARAM wp, LPARAM )
{
auto_ptr<MyData> data(reinterpret_cast<MyData*>(wp));
DisplayeClient(data->client_);
// etc
return 0;
}
这可能是最简单的方法,而且在线程安全的意义上也很健壮。因为您将数据的所有权传递给了主线程,所以没有争用。
这种方法的最大缺点是规模限制。这依赖于 Windows 消息泵在线程之间移动数据。几乎总是,这不是问题。但是 Windows 消息队列可以处理的消息数量是有限制的:
There is a limit of 10,000 posted messages per message queue.
( reference )
同样,对于大多数应用程序来说,这不是问题。
2) 队列用户APC()
异步过程调用 (APC) 是在特定线程的上下文中异步执行的函数。 ( Link ) 如果有一个函数 ProcessIncomingData()
你想在主线程上执行,但你想从工作线程触发它,你可以相当直接地调用该函数使用 QueueUserAPC()
的方式。
与 PostMessage()
方法一样,您从在堆上实例化的自定义数据类型开始:
struct MyData
{
string client_;
string message_type_;
string payload_;
};
// ...
MyData* data = new MyData;
data->client_ = "hoser";
定义用户 APC,记住获取传入数据的所有权:
VOID CALLBACK ProcessIncomingData(ULONG_PTR in)
{
auto_ptr<MyData> data(reinterpret_cast<MyData*>(in));
// magic happens
}
然后您将异步过程调用排队。使用 PostMessage()
方法,您需要主线程的窗口 HWND。在这里,您需要主线程的实际线程 HANDLE。
HANDLE main_thread = my_thread_params.main_thread_handle_;
QueueUserAPC(ProcessIncomingData, main_thread, reinterpret_cast<ULONG_PTR>(data));
有一个重要警告。为了让您的 APC 被主线程调用,主线程必须处于 alertable 等待状态。当您调用 WaitEx() 函数之一(例如 WaitForMultipleObjectsEx())时,您进入了可警告的等待状态。将“alertable”标志设置为 true。
问题是 GUI 线程几乎不应该处于可警告的等待状态,因为您几乎不应该等待。在主线程中等待会阻塞消息泵,使您的应用程序看起来像死机一样。这真是太糟了。为了完整起见,我包括了这个方法——您经常需要在两个工作线程(非 GUI)之间进行通信,这通常是最有效的方法。
关于c++ - 由于后台线程上的事件,在 ui 线程上执行方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3783713/