继续分析await和async

   [TestClass]
    public class AsyncTest
    {

        [TestMethod]
        public async Task TestMain()
        {
            TestAwaitVoid();
            await TestAsync();
            Console.WriteLine("TestMain complete");
        }


        public async Task TestAsync()
        {
            await Task.Delay(2000);
            Console.WriteLine("TestAsync complete");
        }

        public async Task TestAwaitTask()
        {
            Console.WriteLine("TestAwaitTask start");
            await Task.Delay(5000);
            Console.WriteLine("TestAwaitTask complete");
        }

        public async void TestAwaitVoid()
        {
            Console.WriteLine("TestAwaitVoid start");
            await Task.Delay(5000);
            Console.WriteLine("TestAwaitVoid complete");
        }

        public void TestWaitVoid()
        {
            Console.WriteLine("TestWaitVoid start");
            Task.Delay(5000).Wait();
            Console.WriteLine("TestWaitVoid complete");
        }
    }

先看一个例子:

TestAwaitVoid()

结果 StandardOutput:
TestAwaitVoid start
TestAsync complete
TestMain complete

TestWaitVoid()

结果 StandardOutput:
TestWaitVoid start
TestWaitVoid complete
TestAsync complete
TestMain complete

TestAwaitTask()

结果 StandardOutput:
TestAwaitTask start
TestAwaitTask complete
TestAsync complete
TestMain complete

看 TestAwaitVoid()和 TestWaitVoid()  “await task;”和”task.Wait”效果一样吗?

不.“task.Wait()”是一个同步,可能阻塞的调用。它不会立刻返回到Wait()的调用者,直到这个任务进入最终状态,这意味着已进入RanToCompletion,Faulted,或Canceled完成状态。相比之下,”await task;”告诉编译器在”async”标记的方法内部插入一个隐藏的挂起/唤醒点,这样,如果等待的task没有完成,异步方法也会立马返回给调用者,当等待的任务完成时唤醒它从隐藏点处继续执行。当”await task;”会导致比较多应用程序无响应或死锁的情况下使用“task.Wait()”。更多信息请看《Await, and UI, and deadlocks! Oh my!》

当你使用”async”和”await”时,还有其他一些潜在缺陷。Eg:

1)         避免传递lambda表达式的潜在缺陷

2)         保证”async”方法不要被释放

3)         不要忘记完成你的任务

4)         使用”await”依然可能存在死锁?

所以可以看到 TestAwaitVoid() 虽然使用了await但是不会阻塞主线程..依然调用了TestAsync()

看 TestAwaitVoid()和 TestAwaitTask()区别?“await”关键字做了什么

“await”关键字告诉编译器在”async”标记的方法中插入一个可能的挂起/唤醒点。

         逻辑上,这意味着当你写”await someObject;”时,编译器将生成代码来检查someObject代表的操作是否已经完成。如果已经完成,则从await标记的唤醒点处继续开始同步执行;如果没有完成,将为等待的someObject生成一个continue委托,当someObject代表的操作完成的时候调用continue委托。这个continue委托将控制权重新返回到”async”方法对应的await唤醒点处。

返回到await唤醒点处后,不管等待的someObject是否已经经完成,任何结果都可从Task中提取,或者如果someObject操作失败,发生的任何异常随Task一起返回或返回给SynchronizationContext。

         在代码中,意味着当你写:

public static async Task SimpleBodyAsync() {
  Console.WriteLine("Hello, Async World!");
}

         编译器会生成一个包含 MoveNext 方法的状态机类:

[DebuggerStepThrough]     
public static Task SimpleBodyAsync() {
  <SimpleBodyAsync>d__0 d__ = new <SimpleBodyAsync>d__0();
  d__.<>t__builder = AsyncTaskMethodBuilder.Create();
  d__.MoveNext();
  return d__.<>t__builder.Task;
}
 
[CompilerGenerated]
[StructLayout(LayoutKind.Sequential)]
private struct <SimpleBodyAsync>d__0 : <>t__IStateMachine {
  private int <>1__state;
  public AsyncTaskMethodBuilder <>t__builder;
  public Action <>t__MoveNextDelegate;
 
  public void MoveNext() {
    try {
      if (this.<>1__state == -1) return;
      Console.WriteLine("Hello, Async World!");
    }
    catch (Exception e) {
      this.<>1__state = -1;
      this.<>t__builder.SetException(e);
      return;
    }
 
    this.<>1__state = -1;
    this.<>t__builder.SetResult();
  }
 
  ...
}

在实例 someObject上使用这些成员来检查该对象是否已完成(通过 IsCompleted),如果未完成,则挂接一个续体(通过 OnCompleted),当所等待实例最终完成时,系统将再次调用 MoveNext 方法,完成后,来自该操作的任何异常将得到传播或作为结果返回(通过 GetResult),并跳转至上次执行中断的位置。

当使用了await TestAwaitVoid()时,此时的主线程依然没有被阻塞,只是后面的代码都被封装到了状态机中..所以结果看起来和Task.Wait一样

http://www.cnblogs.com/heyuquan/archive/2013/04/26/3045827.html

发表评论