c++ win32: executing a method on ui thread due to an event on background thread

RP4*_*RP4 3 c++ user-interface winapi asynchronous

I've got a background thread that is polling a server.
When there's data, i want to handle the data on the ui thread.

If I store the hwnd of the main window.

How can I get a particular method -- static void DataHandler(void*data) to be executed on the ui thread.

我认为创建一个传递hwnd和函数指针的计时器将起作用.
但有更好的方法吗?我可以使用postmessage以某种方式调用datahandler.

另外,我不是在编写ui代码,所以我没有能力修改消息循环中的任何内容.

Joh*_*ing 25

我经常使用两种主要方法在线程之间进行通信.

1)PostMessage()

创建自定义Windows消息,ala:

#define WM_YOU_HANVE_DATA WM_USER + 101
Run Code Online (Sandbox Code Playgroud)

创建一个自定义数据类型,用于保存要发送到主线程进行处理的数据:

struct MyData
{
  string client_;
  string message_type_;
  string payload_;
};
Run Code Online (Sandbox Code Playgroud)

从您的工作线程,实例化MyData 堆上的副本,填充它,并将其发送到主线程:

MyData* data = new MyData;
data->client_ = "hoser";
// ... etc
PostMessage(main_wnd_handle, WM_YOU_HAVE_DATA, reinterpret_cast<WPARAM>(data), );
Run Code Online (Sandbox Code Playgroud)

在主线程中,处理此消息并以适当的方式处理数据.

BEGIN_MESSAGE_MAP(MyAppWindow, CDialogEx)
        // ...  stuff's going to already be here
        ON_MESSAGE(WM_YOU_HAVE_DATA, OnYouHaveData)
END_MESSAGE_MAP()

// ...
Run Code Online (Sandbox Code Playgroud)

一个重要的注意事项:MyAppWindow主线程现在拥有指向的内存MyData*,所以你必须拥有它的所有权.我这样做auto_ptr:

LRESULT MyAppWindow::OnYouHaveData(WPARAM wp, LPARAM )
{
  auto_ptr<MyData> data(reinterpret_cast<MyData*>(wp));
  DisplayeClient(data->client_);
  // etc
  return 0;  
}
Run Code Online (Sandbox Code Playgroud)

这可能是最简单的方法,它在线程安全的意义上也很强大.因为您将数据的所有权传递给主线程,所以没有争用.

这种方法的最大缺点是规模的限制.这依赖于Windows消息泵来在线程之间移动数据.几乎总是,这不是问题.但是Windows消息队列可以处理的消息数量有限制:

每个消息队列的发布消息数限制为10,000.

(参考)

同样,对于大多数应用来说,这没有问题.

2)QueueUserAPC()

异步过程调用(APC)是在特定线程的上下文中异步执行的函数.(链接)如果ProcessIncomingData()你想在主线程上执行一个函数,但是你想从工作线程触发它,你可以使用相当直接的方式调用该函数QueueUserAPC().

与该PostMessage()方法一样,您从在堆上实例化的自定义数据类型开始:

struct MyData
{
  string client_;
  string message_type_;
  string payload_;
};

// ...

MyData* data = new MyData;
data->client_ = "hoser";
Run Code Online (Sandbox Code Playgroud)

定义用户APC,记住获取传入数据的所有权:

VOID CALLBACK ProcessIncomingData(ULONG_PTR in)
{
  auto_ptr<MyData> data(reinterpret_cast<MyData*>(in));
  // magic happens
}
Run Code Online (Sandbox Code Playgroud)

然后排队异步过程调用.使用该PostMessage()方法,您需要主线程的窗口HWND.在这里,您需要主线程的实际线程HANDLE.

HANDLE main_thread = my_thread_params.main_thread_handle_;
QueueUserAPC(ProcessIncomingData, main_thread, reinterpret_cast<ULONG_PTR>(data));
Run Code Online (Sandbox Code Playgroud)

有一个很大的警告.为了让主线程调用APC,主线程必须处于可警告的等待状态.当您将"alertable"标志设置为true 时,调用WaitEx()等WaitForMultipleObjectsEx()之一时,您将进入可警告的等待状态.

问题是GUI线程几乎从不应该处于可警告的等待状态,因为你几乎不应该等待.等待主线程将阻止消息泵,使您的应用程序似乎冻结.这真是太糟了.我将此方法包含在内以获得完整性 - 您经常需要在两个工作(非GUI)线程之间进行通信,这通常是最有效的方法.