刚开始,我认为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,(这种返回类型我没写过,你自己看着研究)