Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/314.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在ui线程中执行委托(使用消息泵)_C#_.net_Multithreading_Thread Safety_Message Queue - Fatal编程技术网

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

C# 在ui线程中执行委托(使用消息泵),c#,.net,multithreading,thread-safety,message-queue,C#,.net,Multithreading,Thread Safety,Message Queue,我有一个处理与外部服务通信的后台线程。每次后台线程接收到消息时,我都希望将其传递给UI线程进行进一步处理(显示给用户) 目前,我已经创建了一个线程安全的消息队列,该队列在Timer.Tick中定期汇集,并在后台线程中填充。但这个解决方案是次优的 您知道如何使用消息泵将事件从后台线程传递到ui线程吗?您可以使用控件。调用并使用委托。委托将在创建控件的线程上执行 如果GUI线程已被阻止且未处理任何消息,则可以使用Application.DoEvents强制GUI线程处理该线程上所有等待的消息 要将消

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

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


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

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


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


要将消息传送到控件的线程,当然可以使用
控件.BeginInvoke
控件.Invoke
方法,但请注意
控件.Invoke
将在
控件的所属线程当前被阻止时被阻止。

有一些技巧

  • (等)

    我发现这种winforms技术一直都很容易使用,但要注意,有一些微妙的规则需要纠正。我试图捕获一个通用的、可正常工作的实现,该实现可以正确地处理我编写的代码段中的规则

  • 我不太需要使用这个技巧,所以我不能真正地对它说任何有意义的话。然而,你应该知道它是存在的。我相信这是确保在特定线程的上下文中调用某些内容的有效方法,即使该线程不是ui线程

  • 如果使用WPF,WPF控件通常将从
    DispatcherObject
    派生来提供Dispatcher对象。这是一种比
    控件.Invoke()
    功能更丰富的同步技术,但也更复杂。一定要仔细阅读文档


  • 您也可以在WindowsBase.dll中使用WPF调度程序(类调度程序)。

    以下是将WPF中的Dispatcher对象与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
    {
        /// <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");
                }
            }
        }
    }
    
    使用系统;
    使用System.Collections.Generic;
    使用System.Linq;
    使用系统文本;
    使用System.Windows;
    使用System.Windows.Controls;
    使用System.Windows.Data;
    使用System.Windows.Documents;
    使用System.Windows.Input;
    使用System.Windows.Media;
    使用System.Windows.Media.Imaging;
    使用System.Windows.Navigation;
    使用System.Windows.Shapes;
    使用系统消息传递;
    名称空间MSMQGui
    {
    /// 
    ///MainWindow.xaml的交互逻辑
    /// 
    公共部分类主窗口:窗口
    {
    私有字符串queueName=@“\private$\MyFunWithMSMQ”;
    private MessageQueue=null;
    公共主窗口()
    {
    初始化组件();
    如果(!MessageQueue.Exists(queueName))
    MessageQueue.Create(queueName,false);
    队列=新消息队列(队列名称);
    queue.ReceiveCompleted+=ReceiveCompleted;
    }
    私有无效btnAddMessage_单击(对象发送者,路由目标e)
    {
    字符串消息=txtMessage.Text;
    txtMessage.Text=String.Empty;
    队列发送(消息);
    MessageBox.Show(“消息:“+消息+”已发送”);
    }
    私有void填充(对象发送者、路由目标)
    {
    尝试
    {
    queue.BeginReceive(TimeSpan.FromSeconds(1));
    }
    捕获(MessageQueueException)
    {
    MessageBox.Show(“无可用消息”);
    }
    }
    私有void receiveCompleted(对象源,ReceiveCompletedEventArgs e)
    {
    尝试
    {
    var message=queue.EndReceive(e.AsyncResult);
    操作addMessage=(字符串消息)=>{
    ListViewItem=新建ListViewItem();
    项目内容=味精;
    lsvMessages.Items.Add(item);
    };
    this.Dispatcher.Invoke(addMessage,message.Body作为字符串);
    }
    捕获(MessageQueueException)
    {
    MessageBox.Show(“无可用消息”);
    }
    }
    }
    }
    
    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>
    
    
    
    请确保不要将Delegate.Invoke()与Control.Invoke()混淆。。。Control.Invoke()是您想要的
    应用程序。DoEvents
    是一种糟糕的代码味道。我从来没有见过设计良好的代码使用它。当主GUI被阻止时,您使用了什么技术来强制线程消息继续泵送到GUI线程?winforms,但我想听听这两种扩展方法,这让我思考了很多。谢谢