public static partial class TaskExtensions
{
public static IEnumerable<Task<T>> InCompletionOrder<T>(this IEnumerable<Task<T>> source)
{
var inputs = source.ToList();
var boxes = inputs.Select(x => new TaskCompletionSource<T>()).ToList();
int currentIndex = -1;
foreach (var task in inputs)
{
task.ContinueWith(completed =>
//completed 较长一段
{
var nextBox = boxes[Interlocked.Increment(ref currentIndex)];
//boxes Count = 3
PropagateResult(completed, nextBox);
}, TaskContinuationOptions.ExecuteSynchronously);
}
return boxes.Select(box => box.Task);
}
/// <summary>
/// Propagates the status of the given task (which must be completed) to a task completion source
/// (which should not be).
/// </summary>
private static void PropagateResult<T>(Task<T> completedTask,
TaskCompletionSource<T> completionSource)
{
switch (completedTask.Status)
//completedTask很长一段
{
case TaskStatus.Canceled:
completionSource.TrySetCanceled();
break;
case TaskStatus.Faulted:
completionSource.TrySetException(completedTask.Exception.InnerExceptions);
break;
case TaskStatus.RanToCompletion:
completionSource.TrySetResult(completedTask.Result);
//completionSource {System.Threading.Tasks.TaskCompletionSource<string>}
break;
default:
// TODO: Work out whether this is really appropriate. Could set
// an exception in the completion source, of course...
throw new ArgumentException("Task was not completed");
}
}
}
static class MagicOrdering
{
static async Task<int> ShowPageLengthsAsync(params string[] urls)
//urls {string[3]}
{
var tasks = urls.Select(async url =>
//url "http://stackoverflow.com"
{
using (var client = new HttpClient())
//client {System.Net.Http.HttpClient}
{
return await client.GetStringAsync(url);
}
}).ToList();
int total = 0;
foreach (var task in tasks.InCompletionOrder())
{
string page = await task;
//page是 html文件的内容
Console.WriteLine("Got page length {0}", page.Length);
total += page.Length;
//total 5216
}
return total;
}
static void Main() //显示页面长度当数据返回时 在全部返回时收集结果
{
var task = ShowPageLengthsAsync("http://stackoverflow.com", "http://www.google.com", "http://csharpindepth.com");
Console.WriteLine("Total length: {0}", task.Result);
}
}
在全部完成时收集结果
Task.WhenAll是.NET内置的转换构建块,构建自己的方法。创建InterLeaved方法。
传递一个输入任务的序列,并返回一个输出任务的序列。两个序列中任务的结果是相同的,但
存在一个差异,即输出任务的完成顺序与输入完全一样,因此可以一次await一个任务,并
可立即得到任务结果。
依赖TPL中一个非常重要的类型,即TaskCompletionSource<T>。该类型可用于创建一个
尚未含有结果的Task,并在之后提供结果(或异常)。它和AsyncTaskMethodBuilder<T>
都建立在相同的基础结构之上。后者为异步方法提供返回的Task,并在方法体完成时,将
带结果的任务向外传播。
得到一组URL,并行地对每个URL发起请求,输出任务的顺序会与之相同,并在请求完成
时写下各页面的长度,然后返回总长度。
输出
Got page length 5216
Got page length 257025
Exception: System.Reflection.TargetInvocationException: 调用的目标发生了异常。 ---> System.AggregateException: 发生一个 或多个错误。 ---> System.Net.Http.HttpRequestException: 发送请求时出错。 ---> System.Net.WebException: 无法连接到远程服 务器 ---> System.Net.Sockets.SocketException: 由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 31.13.76.16:80
在 System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
在 System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure, Socket s4, Socket s6, Socket& socket, IPAddress& address, ConnectSocketState state, IAsyncResult asyncResult, Exception& exception)
--- 内部异常堆栈跟踪的结尾 ---
在 System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult)
在 System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar)
--- 内部异常堆栈跟踪的结尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
在 Chapter15.MagicOrdering.<<ShowPageLengthsAsync>b__0>d__2.MoveNext() 位置 省略MagicOrdering.cs:行号 67
--- 引发异常的上一位置中堆栈跟踪的末尾 ---
在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
在 System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
在 Chapter15.MagicOrdering.<ShowPageLengthsAsync>d__6.MoveNext() 位置 省略MagicOrdering.cs:行号 74
--- 内部异常堆栈跟踪的结尾 ---
在 System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
在 System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification)
在 System.Threading.Tasks.Task`1.get_Result()
在 Chapter15.MagicOrdering.Main() 位置 MagicOrdering.cs:行号 84
--- 内部异常堆栈跟踪的结尾 ---
在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
在 System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments)
在 System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
在 System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters)
在 MiscUtil.ApplicationChooser.Run(Type type, String[] args)