C# 在 ui 线程中执行委托(使用消息泵)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1132472/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Execute a delegate in the ui thread (using message pump)
提问by Piotr Czapla
I have a background thread that handles communication with an external service. Each time the background thread receives a message I'd like to pass it to the UI thread for further processing (displaying to user).
我有一个处理与外部服务通信的后台线程。每次后台线程收到一条消息时,我想将其传递给 UI 线程以进行进一步处理(向用户显示)。
Currently I've made a thread safe message queue that is pooled periodically in Timer.Tick and filled in the background thread. But this solution is sub optimal.
目前我已经创建了一个线程安全的消息队列,该队列在 Timer.Tick 中定期汇集并填充到后台线程中。但是这个解决方案是次优的。
Do you know how to use message pump to pass events from background thread to ui thread?
您知道如何使用消息泵将事件从后台线程传递到 ui 线程吗?
采纳答案by Greg D
There are a few techniques.
有一些技巧。
Control.Invoke()
(et al)I've found this winforms technique consistently easy to use, but be aware that there are some subtle rules you need to get right. I've tried to capture a general, working implementation that properly handles the rules in a code segment I've posted elsewhere on stackoverflow.
I haven't needed to use this technique much, so I can't really say anything meaningful about it. You should know that it exists, however. I believe that it's an effective way to ensure something gets called in a particular thread's context, even if that thread is notthe ui thread.
If you're working with WPF, the WPF controls will generally derive from
DispatcherObject
to supply the Dispatcher object. This is a more feature-rich synchronization technique than theControl.Invoke()
, but also more complex. Be sure to read the docs carefully.
我发现这种 winforms 技术始终易于使用,但请注意,您需要掌握一些微妙的规则才能正确使用。我试图捕获一个通用的、有效的实现,它可以正确处理我在 stackoverflow 上其他地方发布的代码段中的规则。
我不需要太多使用这种技术,所以我真的不能说任何有意义的事情。但是,您应该知道它存在。我相信这是确保在特定线程的上下文中调用某些内容的有效方法,即使该线程不是ui 线程。
如果您使用 WPF,WPF 控件通常会派生自
DispatcherObject
以提供 Dispatcher 对象。这是一种比 功能更丰富的同步技术Control.Invoke()
,但也更复杂。请务必仔细阅读文档。
回答by Kevin Doyon
You can use Control.Invoke and use a delegate. The delegate will be executed on the thread that created the control.
您可以使用 Control.Invoke 并使用委托。委托将在创建控件的线程上执行。
回答by Mike J
If your GUI thread has blocked and does not process any messages, you could use Application.DoEvents
to force the GUI thread to process all awaiting messages on that thread.
如果您的 GUI 线程被阻塞并且不处理任何消息,您可以使用Application.DoEvents
强制 GUI 线程处理该线程上的所有等待消息。
To pump messages to the thread of the Control, certainly you can use the Control.BeginInvoke
or Control.Invoke
methods but note that Control.Invoke
will block if the owning thread of the Control
is currently blocking.
要将消息泵送到 Control 的线程,您当然可以使用Control.BeginInvoke
orControl.Invoke
方法,但请注意,Control.Invoke
如果 的拥有线程Control
当前正在阻塞,则会阻塞。
回答by Nicolas Dorier
You can use the WPF Dispatcher (class Dispatcher) in WindowsBase.dll too.
您也可以在 WindowsBase.dll 中使用 WPF Dispatcher(类 Dispatcher)。
回答by Nikola Stjelja
Here is an example of using the Dispacther object in WPF with MSMQ.
下面是将 WPF 中的 Dispacther 对象与 MSMQ 一起使用的示例。
The code behind:
背后的代码:
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
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
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<string> 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");
}
}
}
}
The XAML:
XAML:
<Window x:Class="MSMQGui.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="9*"></RowDefinition>
<RowDefinition Height="1*"></RowDefinition>
</Grid.RowDefinitions>
<!-- First row -->
<Label x:Name="lblMessage"
Content="Message:"
HorizontalAlignment="Stretch"
VerticalAlignment="Top"
HorizontalContentAlignment="Right"
Grid.Column="0" Grid.Row="0"
></Label>
<StackPanel Grid.Column="1" Grid.Row="0" Orientation="Horizontal">
<TextBox x:Name="txtMessage" Width="200" HorizontalAlignment="Left" ></TextBox>
<Button x:Name="btnAddMessage" Content="Add message" Margin="5,0,0,0" Click="btnAddMessage_Click"></Button>
</StackPanel>
<!-- Second row -->
<ListView x:Name="lsvMessages" Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0,5,0,0">
</ListView>
<!-- Third row-->
<Button x:Name="btnPopulate" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Right" Click="Populate" Content="Get messages from queque" Margin="5,0,0,0"></Button>
</Grid>
</Window>