async 和 await 出现在C#
5.0事后,给彼此编程带来了过多底造福,特别是当于MVC中的Action也成为async之后,有点起什么都是async的含意了。但是及时吗给我们编程埋下了有隐患,有时候可能会见发局部咱们好尚且未了解怎么发生的Bug,特别是要是连线程基础没有知道的场面下,更不知道哪些去处理了。那今天咱们虽来好看就半兄弟和她俩之叔叔(Task)爷爷(Thread)们究竟有什么分别与特性,本文将会晤指向Thread
到 Task 再到 .NET 4.5的 async和
await,这三种办法下之交互编程作一个概括性的牵线连:开启线程,线程结果回到,线程中止,线程中的酷处理等。

async 和 await 出现在C#
5.0后头,给彼此编程带来了重重之便民,特别是当于MVC中的Action也成为async之后,有点起什么都是async的意味了。但是就为受咱们编程埋下了有隐患,有时候可能会见生局部我们团结一心还未掌握怎么来的Bug,特别是若连线程基础没有明白的景下,更非理解怎么样去处理了。那今天咱们尽管来好好看就简单兄弟和她们之叔叔(Task)爷爷(Thread)们究竟出什么界别与特色,本文将会晤针对Thread
到 Task 再至 .NET 4.5之 async和
await,这三栽办法下之相编程作一个概括性的牵线连:开启线程,线程结果回到,线程中止,线程中的良处理等。

内容索引

  • 创线程
  • 线程池
  • 参数
  • 返回值
  • 共享数据
  • 线程安全
  • Semaphore
  • 可怜处理
  • 一个有点例子认识async &
    await
  • await的原形

内容索引

  • 开创线程
  • 线程池
  • 参数
  • 返回值
  • 共享数据
  • 线程安全
  • Semaphore
  • 挺处理
  • 一个稍例子认识async &
    await
  • await的原形

创建

static void Main(){
    new Thread(Go).Start();  // .NET 1.0开始就有的 
    Task.Factory.StartNew(Go); // .NET 4.0 引入了 TPL
    Task.Run(new Action(Go)); // .NET 4.5 新增了一个Run的方法
}

public static void Go(){
    Console.WriteLine("我是另一个线程");
}

  这中用注意的是,创建Thread的实例之后,需要手动调用它们的Start方法以那个启动。但是于Task来说,StartNew和Run的还要,既会创造新的线程,并且会这启动它。

创建

?

1
2
3
4
5
6
7
8
9
static void Main(){
    new Thread(Go).Start();  // .NET 1.0开始就有的
    Task.Factory.StartNew(Go); // .NET 4.0 引入了 TPL
    Task.Run(new Action(Go)); // .NET 4.5 新增了一个Run的方法
}
 
public static void Go(){
    Console.WriteLine("我是另一个线程");
}

  这中要留意的是,创建Thread的实例之后,需要手动调用它的Start方法以那个启动。但是于Task来说,StartNew和Run的以,既会创造新的线程,并且会当下启动它。

线程池 

  线程的创始是于占资源的同一项工作,.NET
为我们提供了线程池来扶持我们创建同管制线程。Task是默认会直接使用线程池,但是Thread不会见。如果我们无动Task,又想用线程池的话,可以运用ThreadPool类。

static void Main() {
    Console.WriteLine("我是主线程:Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
    ThreadPool.QueueUserWorkItem(Go);

    Console.ReadLine();
}

public static void Go(object data) {
    Console.WriteLine("我是另一个线程:Thread Id {0}",Thread.CurrentThread.ManagedThreadId);
}

线程池 

  线程的开创是于占资源的如出一辙起事情,.NET
为咱提供了线程池来救助我们创建与管制线程。Task是默认会直接使用线程池,但是Thread不见面。如果我们不使Task,又想用线程池的话,可以应用ThreadPool类。

?

1
2
3
4
5
6
7
8
9
10
static void Main() {
    Console.WriteLine("我是主线程:Thread Id {0}", Thread.CurrentThread.ManagedThreadId);
    ThreadPool.QueueUserWorkItem(Go);
 
    Console.ReadLine();
}
 
public static void Go(object data) {
    Console.WriteLine("我是另一个线程:Thread Id {0}",Thread.CurrentThread.ManagedThreadId);
}

图片 1

图片 2

传参数

static void Main() {
    new Thread(Go).Start("arg1"); // 没有匿名委托之前,我们只能这样传入一个object的参数

    new Thread(delegate(){  // 有了匿名委托之后...
        GoGoGo("arg1", "arg2", "arg3");
    });

    new Thread(() => {  // 当然,还有 Lambada
        GoGoGo("arg1","arg2","arg3");
    }).Start();

    Task.Run(() =>{  // Task能这么灵活,也是因为有了Lambda呀。
        GoGoGo("arg1", "arg2", "arg3");
    });
}

public static void Go(object name){
    // TODO
}

public static void GoGoGo(string arg1, string arg2, string arg3){
    // TODO
}

传参数

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void Main() {
    new Thread(Go).Start("arg1"); // 没有匿名委托之前,我们只能这样传入一个object的参数
 
    new Thread(delegate(){  // 有了匿名委托之后...
        GoGoGo("arg1", "arg2", "arg3");
    });
 
    new Thread(() => {  // 当然,还有 Lambada
        GoGoGo("arg1","arg2","arg3");
    }).Start();
 
    Task.Run(() =>{  // Task能这么灵活,也是因为有了Lambda呀。
        GoGoGo("arg1", "arg2", "arg3");
    });
}
 
public static void Go(object name){
    // TODO
}
 
public static void GoGoGo(string arg1, string arg2, string arg3){
    // TODO
}

返回值

  Thead是免克回回值的,但是当再尖端的Task当然如果弥补一下者职能。

static void Main() {
    // GetDayOfThisWeek 运行在另外一个线程中
    var dayName = Task.Run<string>(() => { return GetDayOfThisWeek(); });
    Console.WriteLine("今天是:{0}",dayName.Result);
}

返回值

  Thead是无可知回去回值的,但是作为再尖端的Task当然要弥补一下这效应。

?

1
2
3
4
5
static void Main() {
    // GetDayOfThisWeek 运行在另外一个线程中
    var dayName = Task.Run<string>(() => { return GetDayOfThisWeek(); });
    Console.WriteLine("今天是:{0}",dayName.Result);
}

共享数据

  上面说了参数和归值,我们来拘禁一下线程之间共享数据的问题。

private static bool _isDone = false;    
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
}

static void Done(){
    if (!_isDone) {
        _isDone = true; // 第二个线程来的时候,就不会再执行了(也不是绝对的,取决于计算机的CPU数量以及当时的运行情况)
        Console.WriteLine("Done");
    }
}

 图片 3

  线程之间可经static变量来共享数据。

共享数据

  上面说了参数和归值,我们来拘禁一下线程之间共享数据的问题。

1
2
3
4
5
6
7
8
9
10
11
12
private static bool _isDone = false;   
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
}
 
static void Done(){
    if (!_isDone) {
        _isDone = true; // 第二个线程来的时候,就不会再执行了(也不是绝对的,取决于计算机的CPU数量以及当时的运行情况)
        Console.WriteLine("Done");
    }
}

 图片 4

  线程之间可经static变量来共享数据。

线程安全

   我们先行将地方的代码小小的调整一下,就懂得呀是线程安全了。我们拿Done方法吃之少数句话对易了一晃位置

private static bool _isDone = false;    
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
    Console.ReadLine();
}

static void Done(){
    if (!_isDone) {
       Console.WriteLine("Done"); // 猜猜这里面会被执行几次?
        _isDone = true; 
    }
}

图片 5 

  上面这种状况不会见一直有,但是一旦你命好的讲话,就见面中奖了。因为第一只线程还没来得及拿_isDone设置成true,第二单线程就进入了,而及时不是咱们怀念使的结果,在差不多个线程下,结果未是咱的料结果,这虽是线程不安全。

线程安全

   我们事先把上面的代码小小的调整一下,就理解什么是线程安全了。我们管Done方法中之有限句话对转移了一下位置

?

1
2
3
4
5
6
7
8
9
10
11
12
13
private static bool _isDone = false;   
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
    Console.ReadLine();
}
 
static void Done(){
    if (!_isDone) {
       Console.WriteLine("Done"); // 猜猜这里面会被执行几次?
        _isDone = true;
    }
}

图片 6 

  上面这种气象不见面直接发,但是要您命好之口舌,就会见中奖了。因为第一只线程还从来不来得及拿_isDone设置成true,第二单线程就进来了,而立不是我们怀念使的结果,在多个线程下,结果莫是咱们的预料结果,这就算是线程不安全。

  要解决地方遇到的题材,我们即将用到锁。锁之路有独占锁,互斥锁,以及读写锁等,我们这里就是简单演示一下独占锁。

private static bool _isDone = false;
private static object _lock = new object();
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
    Console.ReadLine();
}

static void Done(){
    lock (_lock){
        if (!_isDone){
            Console.WriteLine("Done"); // 猜猜这里面会被执行几次?
            _isDone = true;
        }
    }
}

  再我们添加锁之后,被吊住的代码在跟一个时空外就同意一个线程访问,其它的线程会吃封堵,只有等到这锁给放走之后外的线程才能够执行于钉住的代码。

  要缓解者遇到的题材,我们将要用到锁。锁之路有独占锁,互斥锁,以及读写锁等,我们这边就是简单演示一下独占锁。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static bool _isDone = false;
private static object _lock = new object();
static void Main(){
    new Thread(Done).Start();
    new Thread(Done).Start();
    Console.ReadLine();
}
 
static void Done(){
    lock (_lock){
        if (!_isDone){
            Console.WriteLine("Done"); // 猜猜这里面会被执行几次?
            _isDone = true;
        }
    }
}

  再我们抬高锁之后,被吊住的代码在同一个时日内就允许一个线程访问,其它的线程会为封堵,只有等到这个锁被释放后外的线程才会实行为钉住的代码。

Semaphore 信号量

  我实际不知底者单词应该怎么翻译,从官的解释来拘禁,我们好这么懂。它可操纵对有平等段落代码或者对某个资源访问的线程的数量,超过这数后,其它的线程就得待,只有当本有线程释放了今后,下面的线程才会看。这个跟锁有类同的效力,只不过不是据的,它同意一定数量的线程同时做客。

static SemaphoreSlim _sem = new SemaphoreSlim(3);    // 我们限制能同时访问的线程数量是3
static void Main(){
    for (int i = 1; i <= 5; i++) new Thread(Enter).Start(i);
    Console.ReadLine();
}

static void Enter(object id){
    Console.WriteLine(id + " 开始排队...");
    _sem.Wait();
    Console.WriteLine(id + " 开始执行!");          
    Thread.Sleep(1000 * (int)id);               
    Console.WriteLine(id + " 执行完毕,离开!");      
    _sem.Release();
}

 

  图片 7

每当尽开始的时光,前3只排队后就当下入实践,但是4和5,只有等到有线程退出后才得履。

Semaphore 信号量

  我其实不懂得此单词应该怎么翻译,从官方的解说来拘禁,我们可这么敞亮。它可操纵对有同截代码或者对某个资源访问的线程的多少,超过此数额后,其它的线程就得等待,只有等今天有线程释放了下,下面的线程才能够顾。这个跟锁有类同之效应,只不过不是独占的,它同意一定数额之线程同时做客。

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static SemaphoreSlim _sem = new SemaphoreSlim(3);    // 我们限制能同时访问的线程数量是3
static void Main(){
    for (int i = 1; i <= 5; i++) new Thread(Enter).Start(i);
    Console.ReadLine();
}
 
static void Enter(object id){
    Console.WriteLine(id + " 开始排队...");
    _sem.Wait();
    Console.WriteLine(id + " 开始执行!");         
    Thread.Sleep(1000 * (int)id);              
    Console.WriteLine(id + " 执行完毕,离开!");     
    _sem.Release();
}

 

  图片 8

在最开始的时,前3独排队后虽立刻入实践,但是4同5,只有等到有线程退出后才得以推行。

万分处理

  其它线程的老大,主线程可以捕获到么?

public static void Main(){
    try{
        new Thread(Go).Start();
    }
    catch (Exception ex){
        // 其它线程里面的异常,我们这里面是捕获不到的。
        Console.WriteLine("Exception!");
    }
}
static void Go() { throw null; }

  那么升级了之Task呢?

public static void Main(){
    try{
        var task = Task.Run(() => { Go(); });
        task.Wait();  // 在调用了这句话之后,主线程才能捕获task里面的异常

        // 对于有返回值的Task, 我们接收了它的返回值就不需要再调用Wait方法了
        // GetName 里面的异常我们也可以捕获到
        var task2 = Task.Run(() => { return GetName(); });
        var name = task2.Result;
    }
    catch (Exception ex){
        Console.WriteLine("Exception!");
    }
}
static void Go() { throw null; }
static string GetName() { throw null; }

不行处理

  其它线程的可怜,主线程可以捕获到么?

?

1
2
3
4
5
6
7
8
9
10
public static void Main(){
    try{
        new Thread(Go).Start();
    }
    catch (Exception ex){
        // 其它线程里面的异常,我们这里面是捕获不到的。
        Console.WriteLine("Exception!");
    }
}
static void Go() { throw null; }

  那么升级了底Task呢?

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static void Main(){
    try{
        var task = Task.Run(() => { Go(); });
        task.Wait();  // 在调用了这句话之后,主线程才能捕获task里面的异常
 
        // 对于有返回值的Task, 我们接收了它的返回值就不需要再调用Wait方法了
        // GetName 里面的异常我们也可以捕获到
        var task2 = Task.Run(() => { return GetName(); });
        var name = task2.Result;
    }
    catch (Exception ex){
        Console.WriteLine("Exception!");
    }
}
static void Go() { throw null; }
static string GetName() { throw null; }

一个聊例子认识async & await

static void Main(string[] args){
    Test(); // 这个方法其实是多余的, 本来可以直接写下面的方法
    // await GetName()  
    // 但是由于控制台的入口方法不支持async,所有我们在入口方法里面不能 用 await

    Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);
}

static async Task Test(){
    // 方法打上async关键字,就可以用await调用同样打上async的方法
    // await 后面的方法将在另外一个线程中执行
    await GetName();
}

static async Task GetName(){
    // Delay 方法来自于.net 4.5
    await Task.Delay(1000);  // 返回值前面加 async 之后,方法里面就可以用await了
    Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine("In antoher thread.....");
}

图片 9

一个不怎么例子认识async & await

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void Main(string[] args){
    Test(); // 这个方法其实是多余的, 本来可以直接写下面的方法
    // await GetName() 
    // 但是由于控制台的入口方法不支持async,所有我们在入口方法里面不能 用 await
             
    Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);
}
 
static async Task Test(){
    // 方法打上async关键字,就可以用await调用同样打上async的方法
    // await 后面的方法将在另外一个线程中执行
    await GetName();
}
 
static async Task GetName(){
    // Delay 方法来自于.net 4.5
    await Task.Delay(1000);  // 返回值前面加 async 之后,方法里面就可以用await了
    Console.WriteLine("Current Thread Id :{0}", Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine("In antoher thread.....");
}

图片 10

await 的原形

  await后底底行各个 

 图片 11

     感谢 locus的指正, await
之后休见面张开新的线程(await
从来不会被新的线程),所以地方的觊觎是起少数问题之。

  await
不见面张开新的线程,当前线程会一直朝着生移动直到碰到真正的Async方法(比如说HttpClient.GetStringAsync),这个办法的里会因此Task.Run或者Task.Factory.StartNew
去开线程。也就是要艺术不是.NET为我们提供的Async方法,我们得团结创立Task,才见面真的去创造线程

static void Main(string[] args)
{
    Console.WriteLine("Main Thread Id: {0}\r\n", Thread.CurrentThread.ManagedThreadId);
    Test();
    Console.ReadLine();
}

static async Task Test()
{
    Console.WriteLine("Before calling GetName, Thread Id: {0}\r\n", Thread.CurrentThread.ManagedThreadId);
    var name = GetName();   //我们这里没有用 await,所以下面的代码可以继续执行
    // 但是如果上面是 await GetName(),下面的代码就不会立即执行,输出结果就不一样了。
    Console.WriteLine("End calling GetName.\r\n");
    Console.WriteLine("Get result from GetName: {0}", await name);
}

static async Task<string> GetName()
{
    // 这里还是主线程
    Console.WriteLine("Before calling Task.Run, current thread Id is: {0}", Thread.CurrentThread.ManagedThreadId);
    return await Task.Run(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("'GetName' Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
        return "Jesse";
    });
}

图片 12

  我们再次来拘禁一下那张图:

  图片 13

  1. 登主线程开实施
  2. 调用async方法,返回一个Task,注意这上另外一个线程已经开始运行,也就是是GetName里面的
    Task
    已经上马工作了
  3. 主线程继续向生活动
  4. 第3步和第4步是还要拓展的,主线程并没有悬挂于等
  5. 倘若其他一个线程已经履行了,name.IsCompleted=true,主线程仍然不用挂起,直接以结果虽得了。如果另外一个线程还与来实践完毕,
    name.IsCompleted=false,那么主线程会挂起等待,直到回到结果得了。

除非async方法以调用前才会加await么?

static void Main(){
    Test();
    Console.ReadLine();
}

static async void Test(){
    Task<string> task = Task.Run(() =>{
        Thread.Sleep(5000);
        return "Hello World";
    });
    string str = await task;  //5 秒之后才会执行这里
    Console.WriteLine(str);
}

  答案非常显然:await并无是针对性于async的计,而是对async方法所返给咱们的Task,这也是干吗有的async方法还必返回给咱Task。所以我们一致可以Task前面为长await关键字,这样做实在是报告编译器我待等是Task的返回值或者当是Task执行了后才会连续为下活动。

并非await关键字,如何确认Task执行完毕了?

static void Main(){
    var task = Task.Run(() =>{
        return GetName();
    });

    task.GetAwaiter().OnCompleted(() =>{
        // 2 秒之后才会执行这里
        var name = task.Result;
        Console.WriteLine("My name is: " + name);
    });

    Console.WriteLine("主线程执行完毕");
    Console.ReadLine();
}

static string GetName(){
    Console.WriteLine("另外一个线程在获取名称");
    Thread.Sleep(2000);
    return "Jesse";
}

图片 14

Task.GetAwaiter()和await Task 的区别?

 图片 15

  • 增长await关键字后,后面的代码会让挂起等待,直到task执行完毕有返回值的下才会持续朝着下实施,这一段时间主线程会处于挂于状态。
  • GetAwaiter方法会返回一个awaitable的对象(继承了INotifyCompletion.OnCompleted方法)我们只是传递了一个寄进去,等task完成了不畏会尽这个委托,但是连无见面潜移默化主线程,下面的代码会立马执行。这吗是胡我们结果里面第一词话会是
    “主线程执行了”!

Task如何为主线程挂起等待?

  点的右是属没有挂到于主线程的情,和我们的await仍然有好几别,那么在获Task的结果前如何挂于主线程呢?

static void Main(){
    var task = Task.Run(() =>{
        return GetName();
    });

    var name = task.GetAwaiter().GetResult();
    Console.WriteLine("My name is:{0}",name);

    Console.WriteLine("主线程执行完毕");
    Console.ReadLine();
}

static string GetName(){
    Console.WriteLine("另外一个线程在获取名称");
    Thread.Sleep(2000);
    return "Jesse";
}

  图片 16

Task.GetAwait()方法会被我们回来一个awaitable的目标,通过调用这个目标的GetResult方法就是会挂于主线程,当然也未是颇具的情况还见面挂于。还记我们Task的风味也?
在平初始的时节便开行了其它一个线程去履行此Task,当我们调用她的结果的早晚要此Task已经履行完毕,主线程是不用等待可以一直将其结果的,如果没实行了那主线程就得挂起等待了。

await 实质是以调用awaitable对象的GetResult方法

static async Task Test(){
    Task<string> task = Task.Run(() =>{
        Console.WriteLine("另一个线程在运行!");  // 这句话只会被执行一次
        Thread.Sleep(2000);
        return "Hello World";
    });

    // 这里主线程会挂起等待,直到task执行完毕我们拿到返回结果
    var result = task.GetAwaiter().GetResult();  
    // 这里不会挂起等待,因为task已经执行完了,我们可以直接拿到结果
    var result2 = await task;     
    Console.WriteLine(str);
}

图片 17

顶之结束,await就精神大白了,欢迎点评。Enjoy Coding! 🙂 

await 的原形

  await后底之执行各个 

 图片 18

     感谢 locus的指正, await
之后非见面敞开新的线程(await
从来不会开启新的线程),所以地方的希冀是发生几许题目之。

  await
不见面敞开新的线程,当前线程会一直于生走直到碰到真正的Async方法(比如说HttpClient.GetStringAsync),这个艺术的里边会因此Task.Run或者Task.Factory.StartNew
去开线程。也即是如果措施无是.NET为我们提供的Async方法,我们得和谐创立Task,才见面真正的失创造线程

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static void Main(string[] args)
{
    Console.WriteLine("Main Thread Id: {0}\r\n", Thread.CurrentThread.ManagedThreadId);
    Test();
    Console.ReadLine();
}
 
static async Task Test()
{
    Console.WriteLine("Before calling GetName, Thread Id: {0}\r\n", Thread.CurrentThread.ManagedThreadId);
    var name = GetName();   //我们这里没有用 await,所以下面的代码可以继续执行
    // 但是如果上面是 await GetName(),下面的代码就不会立即执行,输出结果就不一样了。
    Console.WriteLine("End calling GetName.\r\n");
    Console.WriteLine("Get result from GetName: {0}", await name);
}
 
static async Task<string> GetName()
{
    // 这里还是主线程
    Console.WriteLine("Before calling Task.Run, current thread Id is: {0}", Thread.CurrentThread.ManagedThreadId);
    return await Task.Run(() =>
    {
        Thread.Sleep(1000);
        Console.WriteLine("'GetName' Thread Id: {0}", Thread.CurrentThread.ManagedThreadId);
        return "Jesse";
    });
}

图片 19

  我们再次来拘禁一下那么张图:

  图片 20

  1. 上主线程开施行
  2. 调用async方法,返回一个Task,注意这个时候另外一个线程已经开始运行,也就算是GetName里面的
    Task
    已经起来工作了
  3. 主线程继续往生移动
  4. 第3步和第4步是以拓展的,主线程并没有挂到于等
  5. 倘若其他一个线程已经推行了,name.IsCompleted=true,主线程仍然不用挂起,直接以结果就足以了。如果另外一个线程还同出实行完毕,
    name.IsCompleted=false,那么主线程会挂起等待,直到回到结果得了。

惟有async方法在调用前才会加await么?

?

1
2
3
4
5
6
7
8
9
10
11
12
13
static void Main(){
    Test();
    Console.ReadLine();
}
 
static async void Test(){
    Task<string> task = Task.Run(() =>{
        Thread.Sleep(5000);
        return "Hello World";
    });
    string str = await task;  //5 秒之后才会执行这里
    Console.WriteLine(str);
}

  答案非常显眼:await并无是指向于async的道,而是针对async方法所返给咱的Task,这为是为什么有的async方法都不能不回到给我们Task。所以我们一样好以Task前面为丰富await关键字,这样做实在是告编译器我待等这Task的返回值或者当是Task执行完毕后才会继续朝着生移动。

决不await关键字,如何确认Task执行了了?

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void Main(){
    var task = Task.Run(() =>{
        return GetName();
    });
 
    task.GetAwaiter().OnCompleted(() =>{
        // 2 秒之后才会执行这里
        var name = task.Result;
        Console.WriteLine("My name is: " + name);
    });
 
    Console.WriteLine("主线程执行完毕");
    Console.ReadLine();
}
 
static string GetName(){
    Console.WriteLine("另外一个线程在获取名称");
    Thread.Sleep(2000);
    return "Jesse";
}

图片 21

Task.GetAwaiter()和await Task 的区别?

 图片 22

  • 长await关键字之后,后面的代码会受挂起等待,直到task执行完毕有返回值的时刻才会持续朝下执行,这一段时间主线程会处于挂于状态。
  • GetAwaiter方法会返回一个awaitable的靶子(继承了INotifyCompletion.OnCompleted方法)我们只是传递了一个托进去,等task完成了就是会见履行之委托,但是连切莫见面潜移默化主线程,下面的代码会马上实施。这也是怎我们结果其中第一词话会是
    “主线程执行了”!

Task如何让主线程挂起等待?

  面的右侧是属尚未挂到于主线程的事态,和我们的await仍然有一些别,那么以获得Task的结果前什么挂于主线程呢?

?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
static void Main(){
    var task = Task.Run(() =>{
        return GetName();
    });
 
    var name = task.GetAwaiter().GetResult();
    Console.WriteLine("My name is:{0}",name);
 
    Console.WriteLine("主线程执行完毕");
    Console.ReadLine();
}
 
static string GetName(){
    Console.WriteLine("另外一个线程在获取名称");
    Thread.Sleep(2000);
    return "Jesse";
}

  图片 23

Task.GetAwait()方法会被咱们回来一个awaitable的对象,通过调用这个目标的GetResult方法就是会见挂于主线程,当然也未是兼备的情状尚且见面挂于。还记我们Task的特色也?
在平起来的当儿便开动了外一个线程去履行这Task,当我们调用她的结果的上如果此Task已经推行完毕,主线程是不用等待可以一直将那个结果的,如果无实施了那主线程就得挂起等待了。

await 实质是在调用awaitable对象的GetResult方法

?

1
2
3
4
5
6
7
8
9
10
11
12
13
static async Task Test(){
    Task<string> task = Task.Run(() =>{
        Console.WriteLine("另一个线程在运行!");  // 这句话只会被执行一次
        Thread.Sleep(2000);
        return "Hello World";
    });
 
    // 这里主线程会挂起等待,直到task执行完毕我们拿到返回结果
    var result = task.GetAwaiter().GetResult(); 
    // 这里不会挂起等待,因为task已经执行完了,我们可以直接拿到结果
    var result2 = await task;    
    Console.WriteLine(str);
}

图片 24

至此结束,await就精神大白了,欢迎点评。Enjoy Coding! 🙂 

转自:http://www.cnblogs.com/jesse2013/p/3560999.html

相关文章