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