【问题标题】:Why HttpClient respons is slower in WPF?为什么 HttpClient 响应在 WPF 中较慢?
【发布时间】:2020-12-29 19:39:07
【问题描述】:

我在 Windows Form App(.NET) 中有一种方法,我在其中进行 Rest 调用,反序列化对象中的 json 并在 UI 中显示结果。

所以我将相同的方法从 Windows Form App(.NET) 复制到新的 WPF App(.NET),但 Rest 响应时间发生了变化。 p>

这里是 Rest 响应时间:

Windows Form App(.NET) 以毫秒为单位

WPF App(.NET) 以毫秒为单位

Windows Form App(.NET)中的方法:

TargetFramework

    private Stopwatch Stopwatch = new Stopwatch();
    public Uri url = new Uri("http://xxx.xxx.xxx.xxx");
    private readonly HttpClient _client;

    //constructor
    public Main()
    {
       _client = new HttpClient();
       _client.BaseAddress = url;
       var contentType = new MediaTypeWithQualityHeaderValue("application/json");
       _client.DefaultRequestHeaders.Accept.Add(contentType);
       timer.Start();
       timer.Interval = 1;
     }

 public void Timer_Tick(object sender, EventArgs e)
        {
            timer.Stop();

            try
            {
                 string JsonD= @"{""bal"":{""bla"":0},""data"":{""bla"":""bla""}}";

                _stopwatch.Restart();
                //JsonD is string fot HttpContent
                var contentData = new StringContent(JsonD);
                using (var responseMessage = _client.PostAsync("/xxx/xxx/xxx", contentData).Result)
                {
                    if (responseMessage.IsSuccessStatusCode)
                    {
                        string strContext = responseMessage.Content.ReadAsStringAsync().Result;
                        var result= System.Text.Json.JsonSerializer.Deserialize<Dis>(strContext); 
                    }
                }
             _stopwatch.Stop();
            txbRestResp.Text  = _stopwatch.ElapsedMilliseconds.ToString();
 catch (Exception ex)
            {
                MessageBox.Show(ex.StackTrace.ToString() + " - " + ex.Message);
            }
            timer.Start();
        }

WPF App(.NET)中的方法:

TargetFramework

private Stopwatch Stopwatch = new Stopwatch();

public MainWindow()
{
  InitializeComponent();
  BgWorker = new BackgroundWorker { WorkerReportsProgress = true };
  BgWorker.DoWork += ResetAll;
}

private void ResetAll(object sender, DoWorkEventArgs e)
{
        while (true)
        {
          _stopwatch.Restart();
          Display();
          _stopwatch.Stop();

          lblRestResponse.Content = _stopwatch.ElapsedMilliseconds.ToString();             
        }
}

private void Display()
{ 
        string JsonD= @"{""bal"":{""bla"":0},""data"":{""bla"":""bla""}}";
        string BaseUrl = "http://xxx.xxx.xxx.xxx";
        StringContent httpContentDistanza = new StringContent(JsonD);
        using var client = new HttpClient { BaseAddress = new Uri(BaseUrl) };

        using var responseMessage = client.PostAsync("/xxx/xxx/xxx", httpContentDistanza).Result;
        if (responseMessage.IsSuccessStatusCode)
        {
            string strContext = responseMessage.Content.ReadAsStringAsync().Result;

            var result = System.Text.Json.JsonSerializer.Deserialize<Dis>(strContext);
          }
}

为什么反应之间有这么大的差异? 现在我在 WPF App(.NET) 中工作,我需要像在 Windows Form App(.NET) 中一样获得相同的 Rest 响应。我期待 WPF 应该更好。

我做错了吗?

有什么建议/改进吗?

【问题讨论】:

  • 不确定有多大影响,但您在 WPF 的计时器进程中包含了 HttpClient 的创建,但它已在您的 WinForms 应用程序中创建。尝试将HttpClient 的创建移到您的主要方法中。另外,请确保您没有重新创建多个 HttpClients(看起来不像是)。见:docs.microsoft.com/en-us/azure/architecture/antipatterns/…
  • 您可以考虑在 WPF 中从 BackgroundWorker 切换到 async/await。请参阅:How to run and interact with an async Task from a WPF guiAsync/await vs BackgroundWorker,特别是 this answer
  • 其实我有超过5种方法,比如Display()。我把它放在无限循环中,因为如果 Rest 调用的结果发生变化,我需要知道每一刻。如果结果发生变化,我需要进行一些操作。所以在某种程度上是审问......

标签: c# wpf rest winforms


【解决方案1】:

我做了一个简单的 WPF 程序,它读取 google 主页并显示 HTTP 调用的平均时间。

然后我对一个控制台应用程序做了同样的事情,它读取同一页面 100 次并显示平均时间。

两个应用程序给我的平均时间相同(大约 98 毫秒)。

所以,我将发布我的代码,但主要主题是:不要为每个调用创建一个 HttpClient! Microsoft 建议创建一个 HttpClient 实例并尽可能多地使用它。

这是我的主窗口。

XAML

<StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">
    <Label>Call counter:</Label>
    <TextBlock x:Name="txtCallCounter" />
    <Label>Last call time:</Label>
    <TextBlock x:Name="txtCallTimer" />
    <Label>Average call time:</Label>
    <TextBlock x:Name="txtCallAverage" />
</StackPanel>

后面的代码

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        // Http Client
        client = new HttpClient();

        // Worker
        worker = new BackgroundWorker();
        worker.DoWork += ResetAll;
        worker.RunWorkerAsync();
    }



    /// <summary>
    /// Http client
    /// </summary>
    private HttpClient client { get; }

    /// <summary>
    /// Background worker
    /// </summary>
    private BackgroundWorker worker { get; }

    /// <summary>
    /// Background worker procedure
    /// </summary>
    private void ResetAll(object sender, DoWorkEventArgs e)
    {
        int counter = 0;
        long totalTime = 0;
        long average = 0;
        var stopWatch = new Stopwatch();
        while (true)
        {
            try
            {
                stopWatch.Restart();

                // Read google page
                var responseMessage = client.GetAsync("http://www.google.com").GetAwaiter().GetResult();
                if (responseMessage.IsSuccessStatusCode)
                {
                    var googlePageContent = responseMessage.Content.ReadAsStringAsync().GetAwaiter().GetResult();
                }

                stopWatch.Stop();

                counter++;
                totalTime += stopWatch.ElapsedMilliseconds;
                average = totalTime / counter;

                // Use dispatcher since the worker and UI thread has to be syncronized
                Dispatcher.Invoke(() =>
                {
                    txtCallCounter.Text = counter.ToString();
                    txtCallTimer.Text = stopWatch.ElapsedMilliseconds.ToString();
                    txtCallAverage.Text = average.ToString();
                });

                break;
            }
            // Exceptions not managed due the case that the client or any other object could
            // be disposed on window closing/closed. The best is to manage the
            // background worker in a better way, but it is not the topic of the post
            catch { }
        }
    }


    /// <summary>
    /// On close, clear objects
    /// </summary>
    protected override void OnClosing(CancelEventArgs e)
    {
        base.OnClosing(e);
        worker.DoWork -= ResetAll;
        worker.Dispose();
        client.Dispose();
    }
}

将此代码改编为您的示例(url、请求/响应,...),看看是否效果更好

【讨论】:

  • 谢谢!我在构造中创建了 HttpClient 并将其传递给方法。那改变了很多。从 16 毫秒到 WPF 中的 4 毫秒。
猜你喜欢
  • 1970-01-01
  • 2010-11-11
  • 1970-01-01
  • 2017-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多