在ui线程中执行委托(使用消息泵)

我有一个后台线程来处理与外部服务的通信。 每次后台线程收到消息时,我都想将其传递给UI线程进行进一步处理(显示给用户)。

目前我已经创建了一个线程安全的消息队列,它在Timer.Tick中定期汇集并填充后台线程。 但这种解决方案是次优的。

你知道如何使用消息泵将事件从后台线程传递到ui线程吗?

有一些技巧。

  1. Control.Invoke() (等)

    我发现这种winforms技术一直很容易使用,但请注意,你需要一些微妙的规则才能正确使用。 我试图捕获一个通用的,有效的实现,正确处理我在stackoverflow上发布的代码段中的规则。

  2. SynchronizationContext

    我不需要太多使用这种技术,所以我无法真正说出任何有意义的事情。 但是,您应该知道它存在。 我相信这是确保在特定线程的上下文中调用某些东西的有效方法,即使该线程不是 ui线程。

  3. DispatcherObject.Dispatcher

    如果您正在使用WPF,WPF控件通常派生自DispatcherObject以提供Dispatcher对象。 这是一种比Control.Invoke()function更丰富的同步技术,但也更复杂。 请务必仔细阅读文档。

您可以使用Control.Invoke并使用委托。 委托将在创建控件的线程上执行。

http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx

如果您的GUI线程已被阻止且未处理任何消息,则可以使用Application.DoEvents强制GUI线程处理该线程上的所有等待消息。

要将消息泵送到Control的线程,当然可以使用Control.BeginInvokeControl.Invoke方法,但请注意,如果Control的拥有线程当前正在阻塞, Control.Invoke将阻止。

您也可以在WindowsBase.dll中使用WPF Dispatcher(类Dispatcher)。

以下是在WPF中使用Dispacther对象和MSMQ的示例。

背后的代码:

 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Messaging; namespace MSMQGui { ///  /// Interaction logic for MainWindow.xaml ///  public partial class MainWindow : Window { private string queueName = @".\private$\MyFunWithMSMQ"; private MessageQueue queue = null; public MainWindow() { InitializeComponent(); if (!MessageQueue.Exists(queueName)) MessageQueue.Create(queueName,false); queue = new MessageQueue(queueName); queue.ReceiveCompleted += receiveCompleted; } private void btnAddMessage_Click(object sender, RoutedEventArgs e) { string message = txtMessage.Text; txtMessage.Text = String.Empty; queue.Send(message); MessageBox.Show("Message :" + message + " sent"); } private void Populate(object sender, RoutedEventArgs e) { try { queue.BeginReceive(TimeSpan.FromSeconds(1)) ; } catch (MessageQueueException) { MessageBox.Show("No message available"); } } private void receiveCompleted(object source, ReceiveCompletedEventArgs e) { try { var message=queue.EndReceive(e.AsyncResult); Action addMessage= (string msg) => { ListViewItem item = new ListViewItem(); item.Content = msg; lsvMessages.Items.Add(item); }; this.Dispatcher.Invoke(addMessage, message.Body as string); } catch (MessageQueueException) { MessageBox.Show("No message available"); } } } } 

XAML: