问题描述
我有一个.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;
}));
});
(将Invoke
或BeginInvoke
添加到回调中)
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阻止并且从不返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!