本文介绍了System.Net.Http.HttpClient.PostAsync阻止并且从不返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个.NET框架Windows窗体应用程序,该窗体具有以下代码:

I have a .NET framework Windows Forms application with a form that has this code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace test
{

    public partial class Main : Form
    {

        public int exitCode = 1;
        private Options opts;
        CancellationTokenSource cancellationSource = new CancellationTokenSource();


        public Main(Options opts)
        {
            InitializeComponent();
            this.opts = opts;
        }

        private void btnCancel_Click(object sender, EventArgs e)
        {
            exitCode = 1;
            cancellationSource.Cancel();
            Close();
        }

        async Task doUpload()
        {
            using (var content = new MultipartFormDataContent())
            {
                List<FileStream> streams = new List<FileStream>();
                try
                {
                    foreach (string fPath in opts.InputFiles)
                    {
                        FileStream stream = new FileStream(fPath, FileMode.Open, FileAccess.Read);
                        streams.Add(stream);
                        content.Add(new StreamContent(stream), fPath);
                    }
                    var progressContent = new ProgressableStreamContent(
                         content,
                         4096,
                         (sent, total) =>
                         {
                             double percent = 100 * sent / total;
                             progressBar.Value = (int)percent;
                         });

                    using (var client = new HttpClient())
                    {
                        using (var response = await client.PostAsync(opts.URL, progressContent, cancellationSource.Token))
                        {
                            if (response.IsSuccessStatusCode)
                            {
                                exitCode = 0;
                            }
                            else
                            {
                                MessageBox.Show(
                                    response.Content.ToString(),
                                    "Error " + response.StatusCode,
                                    MessageBoxButtons.OK, MessageBoxIcon.Error
                               );
                            }
                            Close();
                        }
                    }
                }
                finally
                {
                    foreach (FileStream stream in streams)
                    {
                        stream.Close();
                    }
                }
            }

        }

        private void Main_Load(object sender, EventArgs e)
        {
        }

        private void Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = !cancellationSource.IsCancellationRequested;
        }

        private void Main_Shown(object sender, EventArgs e)
        {
            doUpload();
        }
    }
}

ProgressableStreamContent与此处给出的相同:

The ProgressableStreamContent is the same that was given here: C#: HttpClient, File upload progress when uploading multiple file as MultipartFormDataContent

问题在于该响应永远不会返回.换句话说:等待postAsync永远不会完成.同样,进度回调永远也不会被回调.即使我尝试使用包含不存在域的POST URL,也不会发生任何事情.我想这是一个僵局,但我不知道怎么办?异步任务的结果永远不会在任何地方使用,而且也没有等待它的结果.

The problem is that the response is never returned. In other words: await for postAsync never completes. Also, the progress callback is never called back. Even if I try to use a POST URL that contains a non-exsitent domain, nothing happens. I guess it is a deadlock, but I don't see how? The async Task's result is never used anywhere and it is not awaited for.

它不同于导致死锁的async/await示例因为未使用.Result并且从未等待该方法,而且似乎调用ConfigureAwait(false)也没有效果.

It is different from An async/await example that causes a deadlock because .Result is not used and the method is never awaited for, and also it seems that calling ConfigureAwait(false) ha no effect.

更新:我为此问题创建了一个新的github存储库,因此任何人都可以对其进行测试:

UPDATE: I have created a new github repo for this question, so anyone can test it:

https://github.com/nagylzs/csharp_http_post_example

更新:终于可以了.不需要ConfigureAwait.所有UI更新操作必须放在Invoke内部.我已将测试存储库更新为工作版本.还添加了TLSv1.2支持(默认情况下已禁用).

UPDATE: Finally it works. ConfigureAwait is not needed. All UI update operations must be placed inside Invoke. I have updated the test repo to the working version. Also added TLSv1.2 support (which is disabled by default).

推荐答案

PostAsync不会阻止(但是它实际上永远不会返回!).它引发异常:

PostAsync in the code you've posted doesn't block (but it really never returns though!). It throws an exception:

System.InvalidOperationException: Cross-thread operation not valid: Control 'progressBar' accessed from a thread other than the thread it was created on.

这就是断点对您不起作用的原因.正确的解决方案是:

That's the reason for the breakpoints that didn't worked for you. The right solution would be:

var progressContent = new ProgressableStreamContent(
     content,
     4096,
     (sent, total) =>
     {
         Invoke((Action) (() => {
             double percent = 100 * sent / total;
             progressBar.Value = (int) percent;
         }));
     });

(将InvokeBeginInvoke添加到回调中)

HTTP客户端的回调在后台线程上调用,如果要它们访问UI控件,则必须将其放入窗口的偶数队列中.

The callbacks of the HTTP client are called on a background thread, and you have to put them into your window's even queue if you want them to access your UI controls.

.ConfigureAwait(false)与该问题无关,您不应在UI上下文中使用它(恰恰相反:您想要将其放在UI线程上,所以您不应使用它.)

.ConfigureAwait(false) has nothing to do with this issue, you shouldn't use it in UI context (quite the opposite: you want it to put the continuation onto the UI thread, so you shouldn't use it).

这篇关于System.Net.Http.HttpClient.PostAsync阻止并且从不返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-20 23:44