简体   繁体   English

MFC数据通过PostMessage转发到主线程

[英]MFC data forwarding to main thread via PostMessage

I have a C++/MFC application I need to restructure. 我有一个C ++ / MFC应用程序,我需要重组。 The app used to process most of the data on the main thread, therefore blocking the input, and now I want to change it so, that all GUI updates are done through PostMessage. 该应用程序用于处理主线程上的大部分数据,因此阻止输入,现在我想更改它,所有GUI更新都通过PostMessage完成。

Unfortunately, I can't seem to find a good source on how to achieve this goal. 不幸的是,我似乎找不到如何实现这一目标的良好来源。

Right now I'm thinking of creating a priority queue, protected with critical section, a worker thread (while(true)) that processes this queue, and PostMessage mechanism that sends pointers to data to main thread. 现在我正在考虑创建一个优先级队列,使用临界区保护,一个处理此队列的工作线程(while(true)),以及将数据指针发送到主线程的PostMessage机制。

What scares me with this approach is that PostMessage is not guaranteed to arrive at the main thread at all, so, if I understand correctly, there is a chance of memory leak. 使用这种方法让我害怕的是PostMessage根本不能保证到达主线程,所以,如果我理解正确,就有可能发生内存泄漏。

The second problem is that another app can send a custom message to my application, and my application might try to dereference the WPARAM or LPARAM as a pointer thereby causing AV. 第二个问题是另一个应用程序可以向我的应用程序发送自定义消息,我的应用程序可能会尝试取消引用WPARAM或LPARAM作为指针,从而导致AV。

Does anyone know what are the best practices for such tasks? 有谁知道这些任务的最佳实践是什么?

The data can be HTML content for web control, or other content for listboxes, dropdowns, etc. 数据可以是用于Web控件的HTML内容,也可以是列表框,下拉列表等的其他内容。

Your messages will get there. 你的消息将会到达那里。 I'm not sure why you think PostMessage isn't guaranteed to work -- it is. 我不确定为什么你认为PostMessage不能保证工作 - 它是。 (EDIT: Assuming PostMessage() returns TRUE! Check your return codes!) (编辑:假设PostMessage()返回TRUE!检查您的返回代码!)

You want to avoid using a queue to communicate data between the threads. 您希望避免使用队列在线程之间传递数据。 Any queue that is accessed by both threads will need to be protected. 两个线程访问的任何队列都需要受到保护。 Adding hard locks on both sides will serialize your application. 在两侧添加硬锁将序列化您的应用程序。

Instead, create a data structure on the heap using new that contains your data, then tell the other thread "I;ve got data for you, and here it is." 相反,使用包含数据的new在堆上创建一个数据结构,然后告诉另一个线程“我已经为你获取了数据,就在这里。” The recieving thread then takes ownership of that data pointer, and is responsible for delete ing it. 然后,接收线程获取该数据指针的所有权,并负责delete它。 Doing it this way, there are no hard locks. 这样做,没有硬锁。

Now the only trick is figuring out the "tell the other thread" part, but that's easy too. 现在唯一的诀窍是弄清楚“告诉另一个线程”部分,但这也很容易。

If you're sending data from the worker thread to the main thread, just use PostMessage() : 如果要将数据从工作线程发送到主线程,只需使用PostMessage()

worker_thread_proc()
{
// ..

  // Create the data object you're going to pass to the MT
  MyData* data = new MyData;
  data->some_value_ = "foo";

  // Pass it:
  PostMessage(main_wnd, WM_YOU_HAVE_DATA, reinterpret_cast<WPARAM>(data), 0);
}

...the main thread processes this, then deletes the data: ...主线程处理它,然后删除数据:

MainWnd::OnYouHaveData(WPARAM wp, LPARAM)
{
  std::auto_ptr<MyData> data(reinterpret_cast<MyData*>(wp));
  my_widget->set_text(data->some_value_); // you get the idea
}

If you're worried about external apps's custom messages bumping in to yours, you can get Windows to give you a unique message ID using RegisterWindowsMessage() -- your only challenge here is picking the right name for your message. 如果您担心外部应用程序的自定义消息会泄露给您,您可以使用RegisterWindowsMessage()让Windows为您提供唯一的消息ID - 您唯一的挑战是为您的消息选择正确的名称。

If your sending data from the main thread to the worker thread, you can do the same as above, except instead of using PostMessage() to send the data over the wall, you can use either QueueUserAPC() (making sure your worker thead is in an alertable wait state -- read the remarks in the linked docs) or PostThreadMessage() . 如果您将数据从主线程发送到工作线程,您可以执行与上面相同的操作,除了使用PostMessage()通过墙发送数据,您可以使用QueueUserAPC() (确保您的工作人员是处于可警告的等待状态 - 读取链接文档中的备注)或PostThreadMessage()

EDIT: 编辑:

Per your comments in the OP, now I understand why you're concerned about PostMessage() not working. 根据您在OP中的评论,现在我理解为什么您担心PostMessage()不起作用。

Yes, there is a hard limit to the Windows message queue size. 是的,Windows消息队列大小存在硬限制。 By default, there can be only 4,000 messages in the queue. 默认情况下,队列中只能有4,000条消息。 (registry settings can adjust this up to a max of 10,000). (注册表设置可以将其调整为最大10,000)。

If the queue is full, any call to PostMessage() will fail with an error code. 如果队列已满,则对PostMessage()任何调用都将失败并显示错误代码。 When you check GetLastError() (I don't remember which error code it returns right now) it will be clear that the message queue is full. 当你检查GetLastError()(我不记得它现在返回的错误代码)时,很明显消息队列已满。

Not to sound like a mother hen, but you really need to check your return values from API calls. 不要听起来像母鸡,但你真的需要检查API调用的返回值。 But beyond that, if you are running in the the message queue ceiling, I'd say your application is broken anyway. 但除此之外,如果你在消息队列上运行,我会说你的应用程序仍然被破坏了。 When the queue is full, your application won't be able to breathe. 当队列已满时,您的应用程序将无法呼吸。 The screen won't paint, any processing you do will be stale, and all kinds of bad things happen. 屏幕不会画,你做的任何处理都会陈旧,并且会发生各种不好的事情。 If this is the situation you're in, you may need to look at why. 如果您遇到这种情况,您可能需要查看原因。

Use two queues, one for work requests going to the worker thread and one for results going back to the main thread. 使用两个队列,一个用于发送到工作线程的工作请求,另一个用于返回主线程的结果。 You can use PostMessage to wake up the main thread and tell it to check the queue, but you won't need any parameters in the message. 您可以使用PostMessage唤醒主线程并告诉它检查队列,但您不需要消息中的任何参数。

I solved a similar problem some time ago. 我前段时间解决了类似的问题。 I made a singleton queue to hold the data (or actions) that need to flow from background threads to the UI (main) thread. 我创建了一个单例队列来保存需要从后台线程流到UI(主)线程的数据(或操作)。 The queue is protected by a critical section. 队列受临界区保护。 Background thread would place its data in the queue and post a message. 后台线程会将其数据放入队列并发布消息。 The message does not hold any data, it acts as a simple wake-up call "hey, main thread, look at the queue, there's work for you". 该消息不包含任何数据,它充当一个简单的唤醒呼叫“嘿,主线程,看看队列,有你的工作”。

This way, you don't risk leaking any memory or other resources; 这样,您就不会冒泄漏任何内存或其他资源的风险; the queue can be safely destroyed with all the data it contains. 可以使用它包含的所有数据安全地销毁队列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM