C# UploadFileAsync不是异步的?

C# UploadFileAsync不是异步的?,c#,webclient,C#,Webclient,Aight,在这里做了一些谷歌搜索,我发现唯一相关的问题是,虽然它唯一的答案没有被标记为接受,但它是旧的,令人困惑 我的问题基本上是我在标题中所说的。发生的情况是,GUI在上载过程中冻结。我的代码: // stuff above snipped public partial class Form1 : Form { WebClient wcUploader = new WebClient(); public Form1() { InitializeCo

Aight,在这里做了一些谷歌搜索,我发现唯一相关的问题是,虽然它唯一的答案没有被标记为接受,但它是旧的,令人困惑

我的问题基本上是我在标题中所说的。发生的情况是,GUI在上载过程中冻结。我的代码:

// 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 + "%)";
    }
}
编辑:为了澄清,以下是按顺序进行的操作:

  • 我点击按钮1
  • 我选择一个文件
  • GUI停止响应,就像我单击它时什么也没发生一样
  • 几秒钟后,50%显示在文本框aa中,实现点击。请参阅我对标记为解决方案的问题的评论
  • 在GUI没有响应的情况下,大约一秒钟后,它将被响应替换
  • 当然是

    代码运行得很好

    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。在给出的示例中,呼叫总是来自同一个地方,因此不需要检查。为什么不呢