C# UploadFileAsync不是异步的?
Aight,在这里做了一些谷歌搜索,我发现唯一相关的问题是,虽然它唯一的答案没有被标记为接受,但它是旧的,令人困惑 我的问题基本上是我在标题中所说的。发生的情况是,GUI在上载过程中冻结。我的代码:C# UploadFileAsync不是异步的?,c#,webclient,C#,Webclient,Aight,在这里做了一些谷歌搜索,我发现唯一相关的问题是,虽然它唯一的答案没有被标记为接受,但它是旧的,令人困惑 我的问题基本上是我在标题中所说的。发生的情况是,GUI在上载过程中冻结。我的代码: // stuff above snipped public partial class Form1 : Form { WebClient wcUploader = new WebClient(); public Form1() { InitializeCo
// stuff above snipped
public partial class Form1 : Form
{
WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadFileCompletedCallback);
wcUploader.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
}
private void button1_Click(object sender, EventArgs e)
{
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
string toUpload = openFileDialog1.FileName;
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload);
}
}
void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
textBox1.Text = System.Text.Encoding.UTF8.GetString(e.Result);
}
void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend + "b (" + e.ProgressPercentage + "%)";
}
}
编辑:为了澄清,以下是按顺序进行的操作:
wcUploader.UploadFileAsync(…)
启动请求并继续执行,同时在TextBox1中更新进度,完成后我会得到一些JSON
这是异步的。如果您只是调用wcUploader.UploadFile
,执行将在那里阻塞,直到文件上载,您将不会得到任何进度事件
底线:
UI不会被阻止,会调用进度事件并实时更新UI
更新:
要在webclient建立http连接时消除初始块,只需在另一个线程上调用upload。在此场景中,必须使用调用来防止跨线程异常:
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += UploadFileCompletedCallback;
wcUploader.UploadProgressChanged += UploadProgressCallback;
}
private void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = Encoding.UTF8.GetString(e.Result);
button1.Enabled = true;
}));
}
private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend
+ "b (" + e.ProgressPercentage + "%)";
}));
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
button1.Enabled = false;
string toUpload = openFileDialog1.FileName;
textBox1.Text = "Initiating connection";
new Thread(() =>
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload)).Start();
}
}
}
}
当然是
代码运行得很好
wcUploader.UploadFileAsync(…)
启动请求并继续执行,同时在TextBox1中更新进度,完成后我会得到一些JSON
这是异步的。如果您只是调用wcUploader.UploadFile
,执行将在那里阻塞,直到文件上载,您将不会得到任何进度事件
底线:
UI不会被阻止,会调用进度事件并实时更新UI
更新:
要在webclient建立http连接时消除初始块,只需在另一个线程上调用upload。在此场景中,必须使用调用来防止跨线程异常:
using System;
using System.Net;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private readonly WebClient wcUploader = new WebClient();
public Form1()
{
InitializeComponent();
wcUploader.UploadFileCompleted += UploadFileCompletedCallback;
wcUploader.UploadProgressChanged += UploadProgressCallback;
}
private void UploadFileCompletedCallback(object sender, UploadFileCompletedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = Encoding.UTF8.GetString(e.Result);
button1.Enabled = true;
}));
}
private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
// a clever way to handle cross-thread calls and avoid the dreaded
// "Cross-thread operation not valid: Control 'textBox1' accessed
// from a thread other than the thread it was created on." exception
// this will always be called from another thread,
// no need to check for InvokeRequired
BeginInvoke(
new MethodInvoker(() =>
{
textBox1.Text = (string)e.UserState + "\n\n"
+ "Uploaded " + e.BytesSent + "/" + e.TotalBytesToSend
+ "b (" + e.ProgressPercentage + "%)";
}));
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Text = "";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
button1.Enabled = false;
string toUpload = openFileDialog1.FileName;
textBox1.Text = "Initiating connection";
new Thread(() =>
wcUploader.UploadFileAsync(new Uri("http://anyhub.net/api/upload"), "POST", toUpload)).Start();
}
}
}
}
不管UI锁定如何,您的代码中存在一个值得修复的bug: 指定异步上载程序应触发的两个回调。在这些回调中,您将在上传程序的线程上运行;但是,您只能从主GUI线程触摸GUI,因此回调可能会损坏GUI的状态 在两个回调中都不应触摸
textBox1.Text
。这不太可能是问题所在,但无论如何,您应该修复它以避免崩溃和损坏错误。您链接的问题说明了一种避免这种情况的方法:检查表单的控件。InvokeRequired
属性(在后台检查您是否在正确的线程上),或者简单地假设需要调用,然后使用控件。BeginInvoke
在GUI线程上触发一个方法
您的任何控件都可以,因为它们都在同一个线程中运行;因此,
if(textBox1.InvokeRequired)textBox1.BeginInvoke…
与if(this.InvokeRequired)this.BeginInvoke…
无论UI锁定如何,您的代码中都有一个bug值得修复:
指定异步上载程序应触发的两个回调。在这些回调中,您将在上传程序的线程上运行;但是,您只能从主GUI线程触摸GUI,因此回调可能会损坏GUI的状态
在两个回调中都不应触摸textBox1.Text
。这不太可能是问题所在,但无论如何,您应该修复它以避免崩溃和损坏错误。您链接的问题说明了一种避免这种情况的方法:检查表单的控件。InvokeRequired
属性(在后台检查您是否在正确的线程上),或者简单地假设需要调用,然后使用控件。BeginInvoke
在GUI线程上触发一个方法
您的任何控件都可以,因为它们都在同一个线程中运行;所以
if(textBox1.InvokeRequired)textBox1.BeginInvoke…
和if(this.InvokeRequired)this.BeginInvoke…
为什么你认为它不是异步的?为什么你认为它不是异步的?哦。哦哦。我之所以认为它不是异步的,是因为我选择上传的文件太小了,根本没有机会。。。但确实如此,我的大脑很快就把这种进步的表象当成了一个错误。GUI确实冻结了,但在上载开始和结束之间只有几秒钟的时间。您可以通过在另一个线程中调用wcUploader.UploadFileAsync
来消除初始暂停。我将把它添加到我的答案中,但是-然后您必须使用eamon.Oh提到的调用模式。哦哦。我之所以认为它不是异步的,是因为我选择上传的文件太小了,根本没有机会。。。但确实如此,我的大脑很快就把这种进步的表象当成了一个错误。GUI确实冻结了,但在上载开始和结束之间只有几秒钟的时间。您可以通过在另一个线程中调用wcUploader.UploadFileAsync
来消除初始暂停。我将把它添加到我的答案中,但是-然后你必须使用eamon提到的调用模式。不过,我发布的更新代码确实如此。此外,只有在不确定调用源时,才应该检查invokererequired。在给出的示例中,呼叫总是来自同一个地方,因此不需要检查。为什么不呢