码迷,mamicode.com
首页 > 其他好文 > 详细

28.4 异步函数的扩展性

时间:2019-01-01 16:06:30      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:new   ali   case   get   影响   delay   pre   reg   events   

 

    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();   //恢复状态机
        }
    }

 

28.4 异步函数的扩展性

标签:new   ali   case   get   影响   delay   pre   reg   events   

原文地址:https://www.cnblogs.com/kikyoqiang/p/10204880.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!