澳门永利234555com既是会创造新的线程。既会创造新的线程。

既会创建新的线程,既会创建新的线程

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);
}

澳门永利234555com 1

澳门永利234555com 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");
    }
}

 澳门永利234555com 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");
    }
}

 澳门永利234555com 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; 
    }
}

澳门永利234555com 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;
    }
}

澳门永利234555com 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();
}

 

  澳门永利234555com 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();
}

 

  澳门永利234555com 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.....");
}

澳门永利234555com 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.....");
}

澳门永利234555com 10

await 的原形

  await后底之实行顺序 

 澳门永利234555com 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";
    });
}

澳门永利234555com 12

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

  澳门永利234555com 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";
}

澳门永利234555com 14

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

 澳门永利234555com 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";
}

  澳门永利234555com 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);
}

澳门永利234555com 17

到这个结束,await就精神大白了,欢迎点评。Enjoy Coding! 🙂 

await 的原形

  await后底的实行顺序 

 澳门永利234555com 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";
    });
}

澳门永利234555com 19

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

  澳门永利234555com 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";
}

澳门永利234555com 21

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

 澳门永利234555com 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";
}

  澳门永利234555com 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);
}

澳门永利234555com 24

及此结束,await就精神大白了。

原稿出处: 腾飞(Jesse)