【问题标题】:Why does this download the large file, but hang the UI thread?为什么这会下载大文件,但会挂掉UI线程?
【发布时间】:2016-09-26 07:18:28
【问题描述】:
static async void DownloadData(TextBox textboxURL, TextBlock outputView)
{
    try
    {
        using (var client = new HttpClient())
        {
            client.BaseAddress = new Uri(textboxURL.Text);
            client.Timeout = TimeSpan.FromMinutes(1);

            var request = new HttpRequestMessage(HttpMethod.Get, textboxURL.Text);

            /// Fixed thanks to: http://stackoverflow.com/questions/18720435/httpclient-buffer-size-limit-exceeded
            HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            /// Version = response.Version.ToString();
            response.EnsureSuccessStatusCode();
            // Result = await response.Content.ReadAsStringAsync();
            // Task<Stream> inputStream = response.Content.ReadAsStreamAsync();

            /// DUPE CODE: var sendTask = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
            /// NEED TO READ UP ON THIS: response..Result.EnsureSuccessStatusCode();
            var httpStream = await response.Content.ReadAsStreamAsync();

            var picker = new FileSavePicker()
            {
                SuggestedStartLocation = PickerLocationId.Downloads,
                SuggestedFileName = "DOWNLOADING.BIN"
            };

            picker.FileTypeChoices.Add("Any", new List<string>() { "." });
            /// picker.FileTypeChoices.Add("Any", new List<string>() { "*" });

            StorageFile storageFile = await picker.PickSaveFileAsync();

            // Woohoo!  Got it working using await, and removing the Task<> wrapper!
            using (var reader = new StreamReader(httpStream))
            {
                Stream fileStream = await storageFile.OpenStreamForWriteAsync();

                httpStream.CopyTo(fileStream);
                fileStream.Flush();
            }
        }
    }
    catch (Exception ex)
    {
        outputView.Text = "Error, try again!";
        var dlg = new Windows.UI.Popups.MessageDialog(ex.Message, "Error");
        await dlg.ShowAsync();
    }
}

【问题讨论】:

  • 你是怎么调用这个方法的,你可能阻塞了同步上下文,所以阻塞是真实的。还有voidreturn big no,请回复Task
  • 如何让它正确返回一个任务?
  • 请详细说明,不确定您的意思是如何将返回的任务正确映射到调用者。这里Task(非泛型)没有返回值,是对void的更好替代,尤其是在Async方法中有异常时

标签: c# events asynchronous delegates async-await


【解决方案1】:

您在这里使用的是同步 Stream.CopyTo 方法:

httpStream.CopyTo(fileStream);

我想你只是想要:

await httpStream.CopyToAsync(fileStream);

但是,您还应该删除 StreamReader 部分 - 您没有使用 StreamReader,它可能会尝试读取一些数据来检测编码。但是,您应该对存储文件使用using 语句。所以基本上,改变这个:

using (var reader = new StreamReader(httpStream))
{
    Stream fileStream = await storageFile.OpenStreamForWriteAsync();

    httpStream.CopyTo(fileStream);
    fileStream.Flush();
}

到:

using (Stream fileStream = await storageFile.OpenStreamForWriteAsync())
{
    await httpStream.CopyToAsync(fileStream);
}

【讨论】:

  • 有人能解释一下为什么等待 httpStream.CopyToAsync(fileStream);必须是“等待”,而不仅仅是常规的代码行?
  • @Scott:好吧,如果你不等待它,调用线程将立即转到下一行并关闭流......听起来你可能会受益于阅读什么异步/await 确实如此,它是如何做到的。
  • 是的,我是老派。
  • @Scott:完成这项任务的旧方法是使用BackgroundWorker,但 await 使它方式更简单。就个人而言,我发现 Stephen Cleary 的 Task.Run vs BackgroundWorker 文章为那些试图从 BackgroundWorker 迁移到异步的人提供了一个很好的教程。因为它的框架是对新旧方法的比较,所以对于那些更习惯于旧方法的人来说,它更容易理解。
  • 谢谢。我是 UWP / WinRT 开发的新手。 :-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多