【问题标题】:Asynchronous download not reporting via IProgress.Report异步下载未通过 IProgress.Report 报告
【发布时间】:2014-04-17 13:22:32
【问题描述】:

我面临一个奇怪的问题,我必须承认 - 不明白。我有一个任务,它正在从 Web 异步下载文件:

public async Task DownloadFile(Uri requestUri, string fileName, IProgress<int> progress)
{
    HttpWebRequest request = HttpWebRequest.CreateHttp(requestUri);
    request.Method = "GET";
    request.AllowReadStreamBuffering = false;
    using (WebResponse response = await request.GetResponseAsync())
    using (Stream mystr = response.GetResponseStream())
    {
        StorageFolder local = ApplicationData.Current.LocalFolder;
        StorageFile file = await local.CreateFileAsync(fileName);
        using (Stream fileStream = await file.OpenStreamForWriteAsync())
        {
            const int BUFFER_SIZE = 100 * 1024;
            byte[] buf = new byte[BUFFER_SIZE];
            int bytesread = 0;
            // the problem is here below
            while ((bytesread = await mystr.ReadAsync(buf, 0, BUFFER_SIZE)) > 0)
            {
                await fileStream.WriteAsync(buf, 0, bytesread);
                progress.Report(bytesread);
            }
        }
    }
}

任务正在运行,但正如您所见,它应该报告其进度。

事实证明,问题出在程序到达bytesread = await mystr.ReadAsync(buf, 0, BUFFER_SIZE) 行时——在整个文件下载完成之前,它不会对该线程执行任何其他操作(不仅是BUFFER_SIZE)。完成下载后,while 循环会被触发多次,并从内存中复制流——速度非常快。

奇怪的是,相同的代码在 WP8.0 上运行没有问题,现在我尝试在 WP8.1 上运行它,我遇到了这个问题。有人知道我做错了什么吗?

【问题讨论】:

    标签: c# windows-phone-8 asynchronous async-await windows-phone-8.1


    【解决方案1】:

    要记住的一件事是IProgress&lt;T&gt;.Report 本身是“异步的”。并不是说它返回了Task,而是说进度报告实现在Report 返回时可能还没有完成它的实际进度报告。特别是,Progress&lt;T&gt;.Report 将在调用其委托或事件处理程序之前将进度报告编组到捕获的SynchronizationContext

    通常,这正是您想要的:如果您在 UI 线程上创建 Progress&lt;T&gt;,那么它的委托/事件处理程序将在 UI 线程上执行,您不必担心跨线程封送处理使用进度报告更新 UI 时。

    在这种情况下,我相信您看到的是 WP8.1 的一些激进缓存。微软确实一直在推动 WP 上的缓存。如果我的猜测是正确的,那么您看到的行为是由于 ReadAsync 同步完成(响应已经在 HTTP 缓存中)以及 WriteAsync 同步完成(缓冲文件写入)。在这种情况下,任何awaits 都不会真正让步,因此IProgress&lt;T&gt;.Report 调用只是堆叠起来等待它们在 UI 线程上运行的机会。

    缓存通常只是开发过程中的一个“问题”。如果您确实希望您的最终用户遇到这种情况,您可以将await Task.Yield(); 添加到您的while 循环中。

    【讨论】:

    • 我认为您对缓存的猜测是正确的。我调试了更多,我看到的是程序在 ReadAsync 处停止(可能将所有内容下载到缓存中),然后立即继续。我不知道在这种情况下该怎么办。我已经禁止了 ReadStreamBuffering,所以它应该可以工作(就像在 WP8 上一样)。
    • 如果你想防止缓存,那么你需要让服务器发送一个关闭缓存的缓存头,或者附加一个唯一/随机值到URL(并确保服务器忽略它)。
    • 我已经尝试过了,问题不在于下载相同的 Url,但是当程序到达bytesread = await mystr.ReadAsync(buf, 0, BUFFER_SIZE)) 行时,它会停止并等待整个文件从网络下载——不仅是@ 987654334@,下载后剩下的操作在内存上执行的速度非常快。
    • 这确实是令人惊讶的行为。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-11
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多