异步下载不通过IProgress.Report报告

时间:2014-04-17 13:22:33

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

我面临一个奇怪的问题,我必须承认 - 不明白。我有一个Task,它正在异步下载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上运行它,我正面临着这个问题。有人知道我做错了什么吗?

1 个答案:

答案 0 :(得分:2)

要记住的一件事是IProgress<T>.Report本身就是&#34;异步&#34;。不是在它返回Task的意义上,但在某种意义上,进度报告实现可能没有在Report返回时完成其实际进度报告。特别是,Progress<T>.Report会在调用其委托或事件处理程序之前将进度报告封送到捕获的SynchronizationContext

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

在这种情况下,我相信你所看到的是WP8.1的一些激进缓存。微软一直在推动WP的缓存。如果我的猜测是正确的,那么您所看到的行为是由于ReadAsync同步完成(响应已经在HTTP缓存中)以及WriteAsync同步完成(缓冲文件写入)。在这种情况下,await s都不会实际产生,所以IProgress<T>.Report调用只是等待它们在UI线程上运行的机会。

缓存通常只是一个&#34;问题&#34;在开发期间。如果您确实希望最终用户遇到这种情况,可以在await Task.Yield();循环中添加while