1

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

4

1 回答 1

3

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

通常,这正是您想要的:如果您Progress<T>在 UI 线程上创建一个,那么它的委托/事件处理程序将在 UI 线程上执行,并且在使用进度报告。

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

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

于 2014-04-17T14:09:10.980 回答