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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 08:58:44  来源:igfitidea点击:

Execute a delegate in the ui thread (using message pump)

c#.netmultithreadingthread-safetymessage-queue

提问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.

有一些技巧。

  1. 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.

  2. SynchronizationContext

    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.

  3. DispatcherObject.Dispatcher

    If you're working with WPF, the WPF controls will generally derive from DispatcherObjectto supply the Dispatcher object. This is a more feature-rich synchronization technique than the Control.Invoke(), but also more complex. Be sure to read the docs carefully.

  1. Control.Invoke()(等)

    我发现这种 winforms 技术始终易于使用,但请注意,您需要掌握一些微妙的规则才能正确使用。我试图捕获一个通用的、有效的实现,它可以正确处理我在 stackoverflow 上其他地方发布的代码段中的规则。

  2. SynchronizationContext

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

  3. DispatcherObject.Dispatcher

    如果您使用 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 并使用委托。委托将在创建控件的线程上执行。

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

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

回答by Mike J

If your GUI thread has blocked and does not process any messages, you could use Application.DoEventsto 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.BeginInvokeor Control.Invokemethods but note that Control.Invokewill block if the owning thread of the Controlis currently blocking.

要将消息泵送到 Control 的线程,您当然可以使用Control.BeginInvokeorControl.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>