我有一个后台线程轮询服务器。当有数据时,我想处理UI线程上的数据。如果我存储主窗口的hwnd
。
如何在UI线程上执行特定方法static void DataHandler(void* data)
?
我认为创建一个传递hwnd
和函数指针的计时器将起作用。但有更好的方法吗?我可以使用PostMessage
以某种方式调用datahandler。
另外,我不是在编写UI代码,因此我无法修改消息循环中的任何内容。
你可以做的一件事 - 使用一个线程间信号对象可能就像一个布尔标志一样简单。当数据出现在服务器轮询线程上时,您可以发信号通知该标志。您可以在UI线程的消息循环中检查此标志。或者,您可以向UI线程发送自定义窗口消息。
现在我重新阅读了您的问题 - 由于您无法更改UI代码,因此这种方法无效。您可以使用WIN32 API添加自己的自定义消息挂钩函数来解决此问题。
我经常使用两种主要方法在线程之间进行通信。
创建自定义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消息队列可以处理的消息数量有限制:
每个消息队列的发布消息数限制为10,000。
(Qazxswpoi)
同样,对于大多数应用来说,这没有问题。
异步过程调用(APC)是在特定线程的上下文中异步执行的函数。 (reference)如果有一个函数Link你想在主线程上执行,但你想从工作线程触发它,你可以使用ProcessIncomingData()
以相当直接的方式调用该函数。
与QueueUserAPC()
方法一样,您从在堆上实例化的自定义数据类型开始:
PostMessage()
定义用户APC,记住获取传入数据的所有权:
struct MyData
{
string client_;
string message_type_;
string payload_;
};
// ...
MyData* data = new MyData;
data->client_ = "hoser";
然后排队异步过程调用。使用VOID CALLBACK ProcessIncomingData(ULONG_PTR in)
{
auto_ptr<MyData> data(reinterpret_cast<MyData*>(in));
// magic happens
}
方法,您需要主线程的窗口HWND。在这里,您需要主线程的实际线程HANDLE。
PostMessage()
有一个很大的警告。为了让主线程调用APC,主线程必须处于可警告的等待状态。当您调用其中一个WaitEx()函数(如HANDLE main_thread = my_thread_params.main_thread_handle_;
QueueUserAPC(ProcessIncomingData, main_thread, reinterpret_cast<ULONG_PTR>(data));
并将“alertable”标志设置为true)时,您将进入可警告的等待状态。
问题是GUI线程几乎从不应该处于可警告的等待状态,因为你几乎不应该等待。等待主线程将阻止消息泵,使您的应用程序似乎冻结。这真是太糟了。我将此方法包含在内以获得完整性 - 您经常需要在两个工作(非GUI)线程之间进行通信,这通常是最有效的方法。
我使用的方法(需要修改UI线程,但我认为所有方法都在某些方面)是定义一个自定义消息ID,它在UI线程中处理。消息格式和处理封装在一个类中,因此主UI线程只需要将消息转发给类处理程序,而不知道特定格式是什么。
之后,这是一个简单的封装任意函数调用的问题(我使用动态分配的boost :: function对象,但还有其他选项),使用自定义消息ID将其传递给主窗口线程,并且处理程序应该得到在主线程的上下文中调用。正如您所指出的那样,WaitForMultipleObjectsEx()有关于自定义消息部分的详细信息;只要确保你传递的数据/对象在堆上分配并在处理函数内释放,如果它将是一个异步调用(例如:PostMessage)。
更新:John在选项1中所说的,除了在UI中的处理程序调用的函数中编写处理程序操作,因此您对UI代码的影响最小,并且如果您更改了数据结构,那么您将传入任何数据结构方式,您不需要更新UI代码。这是我唯一的补充建议。
希望有所帮助。