简体   繁体   English

WCF:即使呼叫是异步的,DNS查找也会冻结UI

[英]WCF: DNS lookup freezes UI despite the call is async

Below is a tiny WPF test app to demonstrate the problem. 下面是一个微型WPF测试应用程序,用于演示该问题。 By design it's a portable app that communicates with its other instances over LAN. 通过设计,它是一种便携式应用程序,可通过LAN与其他实例进行通信。 If I compile it and run on a remote machine, then run another instance on localhost, enter the name of the remote PC and click "Test Connection" it detects the remote WCF service over TCP just fine. 如果我编译它并在远程计算机上运行,​​然后在localhost上运行另一个实例,输入远程PC的名称,然后单击“测试连接”,它将检测到TCP上的远程WCF服务。 But if I enter some garbage name the UI freezes for a few seconds before throwing "No DNS entries exist for host blabla". 但是,如果我输入一些垃圾名称,则UI会冻结几秒钟,然后抛出“主机blabla不存在DNS条目”。 And that's despite the call is supposedly asynchronous. 而且尽管该调用据说是异步的。 Of course, if I make the call on a different thread it's all smooth. 当然,如果我在其他线程上进行调用,一切都会很顺利。

await Task.Run(async () => await channel.TestConnection());

Is there a way to avoid Task.Run()? 有办法避免Task.Run()吗? It's about scalability. 这与可伸缩性有关。 If I need to test online status of hundreds or thousands of computers at once I'd like to avoid spawning new threads on the caller app. 如果我需要一次测试数百台或数千台计算机的联机状态,我希望避免在调用者应用程序中产生新线程。

XAML: XAML:

<Window x:Class="Wcf_Test_Connection.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Wcf_Test_Connection"
        mc:Ignorable="d"
        Title="MainWindow" SizeToContent="WidthAndHeight">
    <Grid>
        <StackPanel Margin="20" Width="150">
            <TextBlock Text="Computer name:"/>
            <TextBox x:Name="ComputerNameBox" Margin="0,10"/>
            <Button x:Name="TestConnectionButton" Content="Test Connection" Click="TestConnectionButton_Click" />
        </StackPanel>
    </Grid>
</Window>

WCF Service (Channel Factory based): WCF服务(基于Channel Factory):

[ServiceContract]
    public interface IAccessPoint
    {
        [OperationContract]
        Task<bool> TestConnection();
    }

    public class AccessPoint : IAccessPoint
    {
        public static int Port = 4848;
        public static string ServiceAddress = "/AccessPoint";

        public static void Configure(ServiceConfiguration config)
        {
            ContractDescription contract = ContractDescription.GetContract(typeof(IAccessPoint));

            ServiceEndpoint basicEndpoint = new ServiceEndpoint(contract,
                new NetTcpBinding(), new EndpointAddress("net.tcp://localhost:" + Port.ToString() + ServiceAddress));
            config.AddServiceEndpoint(basicEndpoint);
        }

        public static IAccessPoint NewChannel(string address)
        {
            NetTcpBinding binding = new NetTcpBinding();
            EndpointAddress endpoint = new EndpointAddress(address);
            ChannelFactory<IAccessPoint> channelFactory = new ChannelFactory<IAccessPoint>(binding, endpoint);
            IAccessPoint channel = channelFactory.CreateChannel();
            return channel;
        }

        public Task<bool> TestConnection()
        {
            return Task.FromResult(true);
        }

Code-Behind: 代码隐藏:

public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            var serviceHost = new ServiceHost(typeof(AccessPoint));
            serviceHost.Open();
        }

        private async void TestConnectionButton_Click(object sender, RoutedEventArgs e)
        {
            this.Background = Brushes.Salmon;

            var channel = AccessPoint.NewChannel("net.tcp://" + ComputerNameBox.Text + ":" + AccessPoint.Port + AccessPoint.ServiceAddress);
            try
            {
                bool result = await channel.TestConnection();
                MessageBox.Show("Connection good");
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Error");
            }
            finally
            {
                var client = (IClientChannel)channel;
                if (client.State != CommunicationState.Closed && client.State != CommunicationState.Faulted)
                    client.Close();
                else client.Abort();
            }

            this.Background = Brushes.White;
        }
    }

If I need to test online status of hundreds or thousands of computers at once I'd like to avoid spawning new threads on the caller app. 如果我需要一次测试数百台或数千台计算机的联机状态,我希望避免在调用者应用程序中产生新线程。

The task pool scales the degree of concurrency dynamically for you. 任务池可为您动态扩展并发程度。 That's why you should create tasks rather than threads. 这就是为什么您应该创建任务而不是线程。

Is there a way to avoid Task.Run() ? 有办法避免Task.Run()吗?

Apparently you need to call the operation on a background thread in order not to block the UI thread. 显然,您需要在后台线程上调用该操作,以免阻塞UI线程。 Remember that an async method runs synchronously like any other method until it hits an await . 请记住, async方法像其他任何方法一样同步运行,直到达到await状态为止。 So depending on how an async method is actually implemented, it might still block. 因此,根据async方法的实际实现方式,它可能仍然会阻塞。 And obviously it does in this case so your best shot is probably to use Task.Run here after all. 显然,在这种情况下确实如此,因此最好的选择可能是使用Task.RunTask.Run在这里运行。 It is either this or using another API. 要么是这个,要么是使用另一个API。

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

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