Sim*_*mon 1 c# asynchronous c#-5.0
我正在阅读Eric Lippert关于C#5新异步功能的博客上的精彩文章系列.在那里,他使用从远程位置获取文档的方法示例,并且一旦检索到,就将它们存档在存储驱动器上.这是他使用的代码:
async Task<long> ArchiveDocumentsAsync(List<Url> urls)
{
long count = 0;
Task archive = null;
for(int i = 0; i < urls.Count; ++i)
{
var document = await FetchAsync(urls[i]);
count += document.Length;
if (archive != null)
await archive;
archive = ArchiveAsync(document);
}
return count;
}
Run Code Online (Sandbox Code Playgroud)
现在想象一下提取文档非常快.所以提取了第一个文档.之后,它开始存档,而第二个文档正在被提取.现在想象一下,第二个文档已被提取,第一个文档仍在存档.这段代码是否会开始提取第三个文档或等到第一个文档存档?
正如Eric在其文章中所说,这段代码由编译器转换为:
Task<long> ArchiveDocuments(List<Url> urls)
{
var taskBuilder = AsyncMethodBuilder<long>.Create();
State state = State.Start;
TaskAwaiter<Document> fetchAwaiter = null;
TaskAwaiter archiveAwaiter = null;
int i;
long count = 0;
Task archive = null;
Document document;
Action archiveDocuments = () =>
{
switch(state)
{
case State.Start: goto Start;
case State.AfterFetch: goto AfterFetch;
case State.AfterArchive: goto AfterArchive;
}
Start:
for(i = 0; i < urls.Count; ++i)
{
fetchAwaiter = FetchAsync(urls[i]).GetAwaiter();
state = State.AfterFetch;
if (fetchAwaiter.BeginAwait(archiveDocuments))
return;
AfterFetch:
document = fetchAwaiter.EndAwait();
count += document.Length;
if (archive != null)
{
archiveAwaiter = archive.GetAwaiter();
state = State.AfterArchive;
//----> interesting part! <-----
if (archiveAwaiter.BeginAwait(archiveDocuments))
return; //Returns if archive is still working => Fetching of next document not done
AfterArchive:
archiveAwaiter.EndAwait();
}
archive = ArchiveAsync(document);
}
taskBuilder.SetResult(count);
return;
};
archiveDocuments();
return taskBuilder.Task;
}
Run Code Online (Sandbox Code Playgroud)
附加问题:
如果执行停止,是否可以继续获取文档?如果有,怎么样?
这段代码是否会开始提取第三个文档或等到第一个文档存档?
它等待.本文的目的是描述控制流如何与转换一起工作,而不是实际描述用于管理fetch-archive操作的最佳系统.
假设你确实有一百个文件需要获取和存档,你真的不在乎它们发生了什么顺序.(*)你可以创建一个新的异步方法"FetchAndArchive",它异步获取一个文档,然后异步存档它.然后,您可以从另一个生成一百个任务的异步方法中调用该方法一百次,每个任务都异步获取一个文档并将其归档.结果该方法是一个代表做这些百级的任务,每个代表做两个任务的工作的工作组合的任务.
在这种情况下,只要其中一个获取操作无法立即生成其结果,就可以运行准备执行其存档步骤的任务之一.
我不想在本文中涉及任务组合器; 我想专注于更简单的控制流程.
(*)你可能会关心它们发生的顺序,如果不是"下载文档并将其归档",那么操作就是"获取本系列中的下一个视频并播放它".您不希望无序播放它们,即使它们可以更有效地到达无序状态.相反,您希望在当前播放时下载下一个.