asyns和await实现传统的异步---------C#

刚开始,我认为async方法不好理解。现在我倒觉得很好理解,并且很好用。

你不需要再用一个线程或者线程池去封装一个方法了。你只需要标明这个方法是async就可以了。

传统的异步是利用回调函数来写的,写过的人都知道,,那样写的结构东一块西一块的,很不好找。

所以在这里整理啦一下asyns和await实现的异步。。。

asyns和await的异步刚开始学的时候不是很好理解。在这里,我在注释中,将细细的标明

 class Program
    {
        // ---------------规范说明-----------------------------
        //1.用async标识的方法即为异步方法,异步方法的返回值只能是void或者Task<object>或者Task。,---返回值是void的方法,不能使用await调用。注意你的返回类型。
        //2.在其他方法中调用异步方法时,可以加await,表示等待这个异步方法结束。 异步的关键点就在这个await
        //3.await标签只能写在异步方法中
        //4.await标识并调用的方法中,也必须要有await才行
        static void Main(string[] args)
        {
            //第一步,写了一个  “1”
            Console.WriteLine("1");
            
            Paint();
            //第三步----2,写了一个  “5”
            Console.WriteLine("5");

            Console.ReadLine();
        }

        //此方法是已经标明是异步方法
        async static void Paint()
        {
            //第二步,写了一个“2”
            Console.WriteLine("2");

            //第三步--1,代码发现await字段,整个方法将直接返回, 并在这个await这里加个标签,所以第三步输出的是“5”
            //等到 RequestBody方法返回值时,再次进入Paint方法中,并且直接从这个await字段处运行,

            //-----------------------------等待一秒---------这时候,已经输出了“5”

            //一秒后RequestBody方法返回值了,RequestBody的返回类型应该是Task<string>,
            //但是,用了await之后,返回值直接就是string
            //并且,输出“4”的操作被await阻塞
            Console.WriteLine(await RequestBody());
            Console.WriteLine("4");
   
        }

        async static Task<string> RequestBody()
        {    
            //被await调用的方法中,必须要有await
            return await Task.Run(() =>
            {
                Thread.Sleep(2000);
                return "3";
            });
        }

 


    }

2016-1-11补充:其实上面的理解比较浅,关于异步函数的返回值有几种类型。是有错的。还有一种类型,是Task,就是本身

下面是我在书上看到的demo,是关于如何给异步函数限定运行时间的。超时之后,异步函数将被取消

第一步,写一个类,里面放拓展方法

public static class CancelTask
    {
        private struct Void { }
        public static async Task<TResult> WithCancellation<TResult>(this Task<TResult> originalTask, CancellationToken ct)
        {
            var cancellTask = new TaskCompletionSource<Void>();
            using (ct.Register(t => ((TaskCompletionSource<Void>)t).TrySetResult(new Void()), cancellTask))
            {
                Task any = await Task.WhenAny(originalTask, cancellTask.Task);
                if (any == cancellTask.Task)
                {
                    ct.ThrowIfCancellationRequested();
                }
                return await originalTask;
            }
        }

        //这个方法不需要显示的写return
        public static async Task WithCancellation(this Task originalTask, CancellationToken ct)
        {
            var cancellTask = new TaskCompletionSource<Void>();
            using (ct.Register(t => ((TaskCompletionSource<Void>)t).TrySetResult(new Void()), cancellTask))
            {
                Task any = await Task.WhenAny(originalTask, cancellTask.Task);
                if (any == cancellTask.Task)
                {
                    ct.ThrowIfCancellationRequested();
                }
              
            }
            await originalTask;
            //这里是可以无返回值的
        }
    }

第二部,写一个log类。书上的log类写的好复杂。我看完瞎了眼。所以你只需要知道它是个log类,给你看运行效果的。软件发布时,不需要它

   public static class TaskLogger
    {
        public enum TaskLogLevel { None, Pending }
        public static TaskLogLevel LogLevel { get; set; }
        public sealed class TaskLogEntry
        {
            public Task Task { get; set; }
            public string Tag { get; set; }
            public DateTime LogTime { get; set; }
            public string CallerMemberName { get; set; }
            public string CallerFilePath { get; set; }
            public Int32 CallerLineNumber { get; set; }
            public override string ToString()
            {
                return String.Format("LogTime={0},Tag={1},Member={2},File={3}({4})", LogTime, Tag ?? "(none)", CallerMemberName, CallerFilePath, CallerLineNumber);
            }
        }
        private static readonly ConcurrentDictionary<Task, TaskLogEntry> s_Log = new ConcurrentDictionary<Task, TaskLogEntry>();
        public static IEnumerable<TaskLogEntry> GetLogEntries() { return s_Log.Values; }
        public static Task<TResult> Log<TResult>(this Task<TResult> task, string tag = null,
            [CallerMemberName]String callerMemberName = null, [CallerFilePath]String callerfilepath = null,
            [CallerLineNumber] Int32 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] Int32 callerlinenumber = -1)
        {
            if (LogLevel == TaskLogLevel.None) return task;
            var logEntry = new TaskLogEntry
            {
                Task = task,
                LogTime = DateTime.Now,
                Tag = tag,
                CallerLineNumber = callerlinenumber,
                CallerFilePath = callerfilepath,
                CallerMemberName = callerMemberName
            };
            s_Log[task] = logEntry;
            task.ContinueWith(t => { TaskLogEntry entry; s_Log.TryRemove(t, out entry); }, TaskContinuationOptions.ExecuteSynchronously);
            return task;
        }

第三部,写一个Go()方法

  class Program
    {
        static void Main(string[] args)
        {
             Go();
            Console.ReadKey();
        }

        public static async Task Go()
        {
#if DEBUG
            //使用TaskLogger会影响内存和性能,所以只在调试生成中使用它
            TaskLogger.LogLevel = TaskLogger.TaskLogLevel.Pending;
#endif
            //初始化3个任务,为了测试TaskLogger,我们显示控制其持续时间
            var tasks = new List<Task>
            {
                //delay的意思是延迟。意思是,如果这个task只用1秒钟就完成了操作。。这里可以延迟到两秒钟或者更久去结束它
                Task.Delay(2000).Log("2s op"),
                  Task.Delay(5000).Log("5s op"),
                    Task.Delay(6000).Log("6s op"),
            };
            try
            {
                //等待全部任务,但在3秒后取消;只有一个任务能按时完成
                //注意:WithCancellation扩展方法的两个重载版本
                await Task.WhenAll(tasks).WithCancellation(new CancellationTokenSource(3000).Token);
                foreach (var op in TaskLogger.GetLogEntries().OrderBy(tle => tle.LogTime))
                {
                    Console.WriteLine(op);
                }

            }
            catch (OperationCanceledException)
            {
                foreach (var op in TaskLogger.GetLogEntries().OrderBy(tle => tle.LogTime))
                {
                    Console.WriteLine(op);
                }
            }
        }
    }

总结一下:关于async和await的关系。

1.给方法前加async后,调用方法时,方法会异步执行,就像在一个线程里面执行一样。 但是,你要是用await调用async方法的话。,这个async方法就会同步执行。会阻塞它后面的操作。

2.async方法可以用三种返回类型。

Task<T>,这个可以返回一个有用的T类型,比如一个string

Task,这些写的时候,方法不能显示的写return,方法会自动返回自身(是不是隐式的返回自身,这个我自己未验证。做到这一步,我是猜的。。你调试的时候,

看下上面WithCancellation方法的返回值就可以知道我猜的对不对了)

void,(这种返回类型我没写过,你自己看着研究)