28.4 非同步函式的擴充套件性
阿新 • • 發佈:2019-01-01
class Program { static void Main() { Go(); Console.ReadKey(); } public static async Task Go() { #if DEBUG //使用會影響效能和記憶體,所以只在除錯生成中啟用它 TaskLogger.LogLevel = TaskLogger.TaskLogLevel.Pending;#endif //初始化為3個任務:為測試TaskLogger,我們顯示控制其持續時間 var tasks = new List<Task> { Task.Delay(2000).Log("2s op"), Task.Delay(5000).Log("5s op"), Task.Delay(6000).Log("6s op") }; try {//等待全部任務,但在 3秒後取消:只有一個任務能按時完成 await Task.WhenAll(tasks).WithCancellation(new CancellationTokenSource(3000).Token); } catch (Exception) { //查詢logger哪些任務尚未完成,按照從等待時間最長到最短的順序排序 foreach (var op in TaskLogger.GetLogEntries().OrderBy(tle => tle.LogTime)) Console.WriteLine(op); } } }public static class TaskLogger { public enum TaskLogLevel { None, Pending } public static TaskLogLevel LogLevel { get; set; } private static readonly ConcurrentDictionary<Task, TaskLogEntry> s_log = new ConcurrentDictionary<Task, TaskLogEntry>(); public static IEnumerable<TaskLogEntry> GetLogEntries() { return s_log.Values; } public sealed class TaskLogEntry { public Task Task { get; internal set; } public string Tag { get; internal set; } public DateTime LogTime { get; internal set; } public string CallerMemberName { get; internal set; } public string CallerFilePath { get; internal set; } public int CallerLineNumber { get; internal set; } public override string ToString() { return string.Format("LogTime={0},Tag={1},Member={2},File={3}({4})", LogTime, Tag ?? "(None)", CallerMemberName, CallerFilePath, CallerLineNumber); } } public static Task<TResult> Log<TResult>(this Task<TResult> task, string tag = null, [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = -1) { return (Task<TResult>)Log((Task)task, tag, callerMemberName, callerFilePath, callerLineNumber); } public static Task Log(this Task task, string tag = null, [CallerMemberName] string callerMemberName = null, [CallerFilePath] string callerFilePath = null, [CallerLineNumber] int callerLineNumber = -1) { if (LogLevel == TaskLogLevel.None) return task; var logEntry = new TaskLogEntry { Task = task, LogTime = DateTime.Now, Tag = tag, CallerMemberName = callerMemberName, CallerFilePath = callerFilePath, CallerLineNumber = callerLineNumber }; s_log[task] = logEntry; task.ContinueWith(t => { TaskLogEntry entry; s_log.TryRemove(t, out entry); }, TaskContinuationOptions.ExecuteSynchronously); return task; } private struct Void { } //因為沒有非泛型的TaskCompletionSource類 public static async Task WithCancellation(this Task originalTask, CancellationToken token) { //建立在CancellationToken被取消時完成的一個Task var cancelTask = new TaskCompletionSource<Void>(); //一旦CancellationToken被取消就完成Task using (token.Register(t => ((TaskCompletionSource<Void>)t).TrySetResult(new Void()), cancelTask)) { //建立在原始Task或CancellationToken Task完成時都完成的一個Task Task any = await Task.WhenAny(originalTask, cancelTask.Task); if (any == cancelTask.Task) token.ThrowIfCancellationRequested(); } //等待原始任務(以同步方式):若任務失敗,等待它將丟擲第一個內部異常 而不是丟擲AggregateException await originalTask; } }
class Program { static void Main() { Go(); } public static void Go() { ShowExceptions(); for (int i = 0; i < 3; i++) { try { switch (i) { case 0: throw new InvalidOperationException(); case 1: throw new ObjectDisposedException(""); case 2: throw new ArgumentOutOfRangeException(); } } catch { } } } private static async void ShowExceptions() { var eventAwaiter = new EventAwaiter<FirstChanceExceptionEventArgs>(); AppDomain.CurrentDomain.FirstChanceException += eventAwaiter.EventRaised; while (true) Console.WriteLine("AppDomain exception:{0}", (await eventAwaiter).Exception.GetType()); } } public sealed class EventAwaiter<TEventArgs> : INotifyCompletion { private ConcurrentQueue<TEventArgs> m_events = new ConcurrentQueue<TEventArgs>(); private Action m_continuation; //狀態機先呼叫這個來獲得awaiter 自己返回自己 public EventAwaiter<TEventArgs> GetAwaiter() { return this; } //告訴狀態機是否發生了任何事件 public bool IsCompleted { get { return m_events.Count > 0; } } //狀態機告訴我們以後要呼叫什麼方法 把它儲存起來 public void OnCompleted(Action continuation) { Volatile.Write(ref m_continuation, continuation); } //狀態機查詢結果 這是await操作符的結果 public TEventArgs GetResult() { TEventArgs e; m_events.TryDequeue(out e); return e; } //如果都引發了事件,多個執行緒可能同時呼叫 public void EventRaised(object sender, TEventArgs eventArgs) { m_events.Enqueue(eventArgs); //如果有一個等待進行的延續任務,該執行緒會執行它 Action continuation = Interlocked.Exchange(ref m_continuation, null); if (continuation != null) continuation(); //恢復狀態機 } }