C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。

为什么呢?我觉得大家的await与async的打开方式不正确。

 正确的打开方式

 
首先看下使用约束。

1、await 只能在标记了async的函数内使用。

2、await 等待的函数必须标记async。

有没有感觉这是个循环?没错,这就是个循环。这也就是为什么大家不怎么用他们的原因。这个循环很讨厌,那么怎么破除这个循环呢?

【很简单,await等待的是线程,不是函数。】

不理解吗?没关系,接着看下去。

下面从头来讲解,首先看这么一组对比

1
2
3
4
5
6
7
8
public static int NoAsyncTest()
{
   return 1;
}
public static async Task<int> AsyncTest()
{
  return 1;
}

 async Task<int>等于int

这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?

目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。

当然不是,那什么时候会让 await AsyncTest()有意义呢?

我们接着往下看,修改AsyncTest如下。然后,此时再调用await AsyncTest(),你会神奇的发现,依然没有卵用。。。

Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static async void Excute()
 {
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
       await AsyncTest();
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
 }
 
 public static async Task<int> AsyncTest()
 {
        Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            return 1;
 }

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

别着急,我们稍作调整,在线程后面增加.GetAwaiter().GetResult()。这句话是干什么用的呢?是用来获取线程返回值的。

这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。

运行一下,我们将看下面的结果。

1
2
3
4
5
6
7
8
9
public static async Task<int> AsyncTest()
        {
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await 

但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。

那么怎么才能让他起作用呢?

首先,我们定义一个普通函数,他的返回值是一个Task,然后我们得到Task后,运行它,再用await等待这个Task。

于是我们就得到这样的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static async void Excute()
       {
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
           var waitTask = AsyncTestRun();
           waitTask.Start();
           int i = await waitTask;
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
       }
       public static Task<int> AsyncTestRun()
       {
           Task<int> t = new Task<int>(() => {
               Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
               Thread.Sleep(1000);
               return 100;
           });
           return t;
       }

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await  

如图,这样写await AsyncTest();就起作用了。

所以,还是那句话,await等待的是线程,不是函数。

但在图里,我们发现很奇怪的一点,结束Excute也是线程3,而不是线程1。也就是说,Await会对线程进行优化。

下面看下两组代码的对比,让我们就更清楚的了解下Await。

第一组,使用await等待线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static async void Excute()
{
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
   await SingleAwait();
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
       
public static async Task SingleAwait()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
     await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 

第二组,使用等待线程结果,等待线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static async void Excute()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await SingleNoAwait();
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }
public static async Task SingleNoAwait()
{
      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
       Task.Run(() =>
        {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

可以明确的看到,第二组,线程重新回到了主线程1中,而第一组,已经被优化到了线程4中。

 

 结语

await是一种很便捷的语法,他的确会让代码简洁一些,但他主动优化线程的功能,如果不了解就使用,可能会导致一些奇怪的BUG发生。

这也是官方为什么只提供了await调用服务的例子,因为,在程序内调用,await还是要了解后,再使用,才安全。

C#语法——委托,架构的血液

C#语法——元组类型

C#语法——泛型的多种应用

----------------------------------------------------------------------------------------------------

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的推荐,非常感谢!

 

 

 

C#基础系列——异步编程初探:async和await

 

前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法。确实,没有异步的多线程是单调的、乏味的,async和await是出现在C#5.0之后,它的出现给了异步并行变成带来了很大的方便。异步编程涉及到的东西还是比较多,本篇还是先介绍下async和await的原理及简单实现。

C#基础系列目录:

 

之前的那篇 C#基础系列——多线程的常见用法详解 就讲到了多线程new Thread()的方式对于有返回值类型的委托是没有解决方案的,如果需要返回值,必须要依靠异步的方式。了解异步之前,我们先来看看Thread对象的升级版本Task对象:

1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static void Main(string[] args)
        {
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine(strRes.Result);//得到方法的返回值
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            Console.ReadLine();
        }

        static string GetReturnResult()
        {
            Thread.Sleep(2000);
            return "我是返回值";
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

先来看结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static void Main(string[] args)
        {
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); });
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes.Result);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            Console.ReadLine();
        }

        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);
            return "我是返回值";
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。

Task对象的用法如下:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
            //用法一
            Task task1 = new Task(new Action(MyAction));
          //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
        //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });

            task1.Start();
            task2.Start();
            task3.Start();
            task4.Start();
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。

 

2、初识 async & await。

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
        static void Main(string[] args)
        {
            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();
        }

        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var name = GetReturnResult();
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }

        static async Task<string> GetReturnResult()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            return await Task.Run(() =>
            {
                Thread.Sleep(3000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

我们来看看程序的执行过程:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由上面的结果可以得到如下结论:

(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。

(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。

(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。

(4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。

 

3、除了await关键字,Task对象还有另外一种方式等待执行结果。  

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var name = GetReturnResult();
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

这样可以得到相同的结果。

name.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
    {
        public bool IsCompleted { get; }
        public TResult GetResult();
        public void OnCompleted(Action continuation);
        public void UnsafeOnCompleted(Action continuation);
   }    
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

IsCompleted:获取一个值,该值指示异步任务是否已完成。

GetResult():得到执行的结果。这个方法和await关键字效果相同。

OnCompleted():传入一个委托,在任务执行完成之后执行。

UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。

由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。

 

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace async异步_Task分派任务_await等待
{
public class Test
{
public async Task TestAsync()
{
await GetAsync();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

}
public async Task GetAsync()
{
await Task.Delay(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
}


class Program
{
//使用 async 关键字定义的异步方法简称为“异步方法”。
//如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
//async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
//不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
//一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
//异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
// 方法签名包含 async 修饰符。
//按照约定,异步方法的名称以“Async”后缀结尾。
//返回类型为下列类型之一:
//如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
//如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
//void:如果要编写异步事件处理程序。
//包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
//方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
static void Main(string[] args)
{
Test t = new Test();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
t.TestAsync();
Console.ReadLine();
}
}
}

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace await与async的正确打开方式
{
    class Program
    {
        static void Main(string[] args)
        {
            //await 只能在标记了async的函数内使用。
            //await 等待的函数必须标记async。
            //【很简单,await等待的是线程,不是函数。】
            Console.WriteLine(Thread.CurrentThread.GetHashCode());
            NoAsyncTest();
            AsyncTest();
            //await AsyncTest();
            Console.WriteLine();
            Excute();
            Console.ReadKey();
        }
        public static int NoAsyncTest()
        {
            return 1;
        }
        public static async Task<int> AsyncTest()//警告    7    此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。
        {// async Task<int>等于int
            //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
            //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
            //当然不是,那什么时候会让 await AsyncTest()有意义呢?
            Task.Run(() =>
            {//线程变化了 创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        public static async void Excute()
        {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await AsyncTest();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }


    }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            Excute2();
            //Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Threading;

namespace msdntask
{
    class Program
    {
        //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
        static void Main(string[] args)
        {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行

            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。
            //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。
            //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes.Result);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕  这段太绝对 跟主线程执行到哪  子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
            //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
            //应该是 子线程值 阻塞主线程     因为主线程要得到返回值,必须要等方法执行完成。     Task.Run 会开子线程一起执行 


            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");

            //用法一
            Task task1 = new Task(new Action(MyAction));
            //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
            //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });
            //Task对象的构造函数传入的是一个委托
            //task1.Start();
            //task2.Start();
            //task3.Start();
            //task4.Start();


            Console.WriteLine("-------00-------");

            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();



            Console.ReadLine();
        }
        static void MyAction()
        {
            Console.WriteLine("MyAction   " + Thread.CurrentThread.ManagedThreadId);
        }
        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //var name = GetReturnResult2();     // 不用  await  不等待   主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法)
            // 加上 await 当异步方法 等待
            var name = await GetReturnResult2();     // 等待 异步方法里面 线程执行完  返回 string
            //加上 await 返回 string 不加Task<TResult>  
            // 不加 普通方法
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }
        static async Task<string> GetReturnResult2()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //return await Task.Run(() =>     // await只限于  当前异步方法等待            
            //return "";
            return await Task.Run(() =>     // await只限于  当前异步方法等待 //错误    4    由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>”            // 这里 Task.Run 会直接创线程执行的       异步操作的Result 才是 值 返回值     这里返回Task<string>
            {
                Thread.Sleep(5000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            //Thread.Sleep(2000);
            Thread.Sleep(10000);
            return "我是返回值";
        }
        //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。
        // await 会等待   线程执行完成  
        // Task.Run 和主线程一起执行  但是如果有返回值 主线程会卡住 等计算完 返回值
    }
}

 

 

 

  task 操作任务会开启子线程 执行任务

task.run 不会改变主流程线程
就不改变线程
我的手机  20:20:32
那就是 不等待 
 
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

Excute3();

public static async void Excute3()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
await SingleNoAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
}

await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行 

 

public static async Task SingleNoAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
//await 不等待

//.GetAwaiter().GetResult();   //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。   把这句拿掉 会创子线程一起 执行  // await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行     加上后 就等待 子线程 执行完 主线程才继续执行
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
});//.GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
return;
}

 

 

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

等10秒

 C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Threading;

namespace msdntask
{
    class Program
    {
        //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
        static void Main(string[] args)
        {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。    //GetAwaiter().GetResult(); 主线程等待子线程10S 主线程继续执行    不加主线程和子线程一起执行
            //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。
            //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕  这段太绝对 跟主线程执行到哪  子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
            //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
            //应该是 子线程值 阻塞主线程     因为主线程要得到返回值,必须要等方法执行完成。     Task.Run 会开子线程一起执行 


            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");

            //用法一
            Task task1 = new Task(new Action(MyAction));
            //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
            //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });
            //Task对象的构造函数传入的是一个委托
            //task1.Start();
            //task2.Start();
            //task3.Start();
            //task4.Start();


            Console.WriteLine("-------00-------");

            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();



            Console.ReadLine();
        }
        static void MyAction()
        {
            Console.WriteLine("MyAction   " + Thread.CurrentThread.ManagedThreadId);
        }
        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //var name = GetReturnResult2();     // 不用  await  不等待   主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法)
            // 加上 await 当异步方法 等待
            var name = await GetReturnResult2();     // 等待 异步方法里面 线程执行完  返回 string
            //加上 await 返回 string 不加Task<TResult>  
            // 不加 普通方法
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
        }
        static async Task<string> GetReturnResult2()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //return await Task.Run(() =>     // await只限于  当前异步方法等待            
            //return "";
            return await Task.Run(() =>     // await只限于  当前异步方法等待 //错误    4    由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>”            // 这里 Task.Run 会直接创线程执行的       异步操作的Result 才是 值 返回值     这里返回Task<string>
            {
                Thread.Sleep(5000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId + "  shijian" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //Thread.Sleep(2000);
            Thread.Sleep(10000);
            return "我是返回值";
        }
        //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。
        // await 会等待   线程执行完成  
        // Task.Run 和主线程一起执行  但是如果有返回值 主线程会卡住 等计算完 返回值
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();   //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。   把这句拿掉 会创子线程一起 执行   await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行     加上后 就等待 子线程 执行完 主线程继续执行

            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace async异步_Task分派任务_await等待
{
    public class Test
    {
        public async Task TestAsync()
        {
            await GetAsync();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            
        }
        public async Task GetAsync()
        {
            await Task.Delay(1);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }
    }

    class Program
    {
        //使用 async 关键字定义的异步方法简称为“异步方法”。
        //如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
        //async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
        //不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
        //一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
        //异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
        //        方法签名包含 async 修饰符。
        //按照约定,异步方法的名称以“Async”后缀结尾。
        //返回类型为下列类型之一:
        //如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
        //如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
        //void:如果要编写异步事件处理程序。
        //包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
        //方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
        static void Main(string[] args)
        {
            Test t = new Test();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            t.TestAsync();
            Console.ReadLine();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace await与async的正确打开方式
{
    class Program
    {
        static void Main(string[] args)
        {
            //await 只能在标记了async的函数内使用。
            //await 等待的函数必须标记async。
            //【很简单,await等待的是线程,不是函数。】
            Console.WriteLine(Thread.CurrentThread.GetHashCode());
            NoAsyncTest();
            AsyncTest();
            //await AsyncTest();
            Console.WriteLine();
            Excute();
            Console.ReadKey();
        }
        public static int NoAsyncTest()
        {
            return 1;
        }
        public static async Task<int> AsyncTest()//警告    7    此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。
        {// async Task<int>等于int
            //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
            //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
            //当然不是,那什么时候会让 await AsyncTest()有意义呢?
            Task.Run(() =>
            {//线程变化了 创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        public static async void Excute()
        {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await AsyncTest();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }


    }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。

}

 

C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。

为什么呢?我觉得大家的await与async的打开方式不正确。

 正确的打开方式

 
首先看下使用约束。

1、await 只能在标记了async的函数内使用。

2、await 等待的函数必须标记async。

有没有感觉这是个循环?没错,这就是个循环。这也就是为什么大家不怎么用他们的原因。这个循环很讨厌,那么怎么破除这个循环呢?

【很简单,await等待的是线程,不是函数。】

不理解吗?没关系,接着看下去。

下面从头来讲解,首先看这么一组对比

1
2
3
4
5
6
7
8
public static int NoAsyncTest()
{
   return 1;
}
public static async Task<int> AsyncTest()
{
  return 1;
}

 async Task<int>等于int

这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?

目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。

当然不是,那什么时候会让 await AsyncTest()有意义呢?

我们接着往下看,修改AsyncTest如下。然后,此时再调用await AsyncTest(),你会神奇的发现,依然没有卵用。。。

Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static async void Excute()
 {
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
       await AsyncTest();
       Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
 }
 
 public static async Task<int> AsyncTest()
 {
        Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            return 1;
 }

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

别着急,我们稍作调整,在线程后面增加.GetAwaiter().GetResult()。这句话是干什么用的呢?是用来获取线程返回值的。

这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。

运行一下,我们将看下面的结果。

1
2
3
4
5
6
7
8
9
public static async Task<int> AsyncTest()
        {
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await 

但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。

那么怎么才能让他起作用呢?

首先,我们定义一个普通函数,他的返回值是一个Task,然后我们得到Task后,运行它,再用await等待这个Task。

于是我们就得到这样的结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static async void Excute()
       {
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
           var waitTask = AsyncTestRun();
           waitTask.Start();
           int i = await waitTask;
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
           Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
       }
       public static Task<int> AsyncTestRun()
       {
           Task<int> t = new Task<int>(() => {
               Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
               Thread.Sleep(1000);
               return 100;
           });
           return t;
       }

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await  

如图,这样写await AsyncTest();就起作用了。

所以,还是那句话,await等待的是线程,不是函数。

但在图里,我们发现很奇怪的一点,结束Excute也是线程3,而不是线程1。也就是说,Await会对线程进行优化。

下面看下两组代码的对比,让我们就更清楚的了解下Await。

第一组,使用await等待线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static async void Excute()
{
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
   await SingleAwait();
   Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
       
public static async Task SingleAwait()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
     await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            await Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 

第二组,使用等待线程结果,等待线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static async void Excute()
{
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await SingleNoAwait();
     Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }
public static async Task SingleNoAwait()
{
      Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
       Task.Run(() =>
        {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

可以明确的看到,第二组,线程重新回到了主线程1中,而第一组,已经被优化到了线程4中。

 

 结语

await是一种很便捷的语法,他的确会让代码简洁一些,但他主动优化线程的功能,如果不了解就使用,可能会导致一些奇怪的BUG发生。

这也是官方为什么只提供了await调用服务的例子,因为,在程序内调用,await还是要了解后,再使用,才安全。

C#语法——委托,架构的血液

C#语法——元组类型

C#语法——泛型的多种应用

----------------------------------------------------------------------------------------------------

注:此文章为原创,欢迎转载,请在文章页面明显位置给出此文链接!
若您觉得这篇文章还不错,请点击下右下角的推荐,非常感谢!

 

 

 

C#基础系列——异步编程初探:async和await

 

前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法。确实,没有异步的多线程是单调的、乏味的,async和await是出现在C#5.0之后,它的出现给了异步并行变成带来了很大的方便。异步编程涉及到的东西还是比较多,本篇还是先介绍下async和await的原理及简单实现。

C#基础系列目录:

 

之前的那篇 C#基础系列——多线程的常见用法详解 就讲到了多线程new Thread()的方式对于有返回值类型的委托是没有解决方案的,如果需要返回值,必须要依靠异步的方式。了解异步之前,我们先来看看Thread对象的升级版本Task对象:

1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static void Main(string[] args)
        {
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine(strRes.Result);//得到方法的返回值
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            Console.ReadLine();
        }

        static string GetReturnResult()
        {
            Thread.Sleep(2000);
            return "我是返回值";
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

先来看结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static void Main(string[] args)
        {
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); });
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes.Result);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            Console.ReadLine();
        }

        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);
            return "我是返回值";
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。

Task对象的用法如下:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
            //用法一
            Task task1 = new Task(new Action(MyAction));
          //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
        //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });

            task1.Start();
            task2.Start();
            task3.Start();
            task4.Start();
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。

 

2、初识 async & await。

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
        static void Main(string[] args)
        {
            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();
        }

        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var name = GetReturnResult();
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }

        static async Task<string> GetReturnResult()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            return await Task.Run(() =>
            {
                Thread.Sleep(3000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

我们来看看程序的执行过程:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由上面的结果可以得到如下结论:

(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。

(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。

(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。

(4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。

 

3、除了await关键字,Task对象还有另外一种方式等待执行结果。  

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var name = GetReturnResult();
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

这样可以得到相同的结果。

name.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
    {
        public bool IsCompleted { get; }
        public TResult GetResult();
        public void OnCompleted(Action continuation);
        public void UnsafeOnCompleted(Action continuation);
   }    
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

IsCompleted:获取一个值,该值指示异步任务是否已完成。

GetResult():得到执行的结果。这个方法和await关键字效果相同。

OnCompleted():传入一个委托,在任务执行完成之后执行。

UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。

由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。

 

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace async异步_Task分派任务_await等待
{
public class Test
{
public async Task TestAsync()
{
await GetAsync();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

}
public async Task GetAsync()
{
await Task.Delay(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
}


class Program
{
//使用 async 关键字定义的异步方法简称为“异步方法”。
//如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
//async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
//不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
//一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
//异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
// 方法签名包含 async 修饰符。
//按照约定,异步方法的名称以“Async”后缀结尾。
//返回类型为下列类型之一:
//如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
//如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
//void:如果要编写异步事件处理程序。
//包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
//方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
static void Main(string[] args)
{
Test t = new Test();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
t.TestAsync();
Console.ReadLine();
}
}
}

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace await与async的正确打开方式
{
    class Program
    {
        static void Main(string[] args)
        {
            //await 只能在标记了async的函数内使用。
            //await 等待的函数必须标记async。
            //【很简单,await等待的是线程,不是函数。】
            Console.WriteLine(Thread.CurrentThread.GetHashCode());
            NoAsyncTest();
            AsyncTest();
            //await AsyncTest();
            Console.WriteLine();
            Excute();
            Console.ReadKey();
        }
        public static int NoAsyncTest()
        {
            return 1;
        }
        public static async Task<int> AsyncTest()//警告    7    此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。
        {// async Task<int>等于int
            //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
            //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
            //当然不是,那什么时候会让 await AsyncTest()有意义呢?
            Task.Run(() =>
            {//线程变化了 创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        public static async void Excute()
        {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await AsyncTest();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }


    }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            Excute2();
            //Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Threading;

namespace msdntask
{
    class Program
    {
        //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
        static void Main(string[] args)
        {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行

            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。
            //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。
            //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes.Result);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕  这段太绝对 跟主线程执行到哪  子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
            //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
            //应该是 子线程值 阻塞主线程     因为主线程要得到返回值,必须要等方法执行完成。     Task.Run 会开子线程一起执行 


            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");

            //用法一
            Task task1 = new Task(new Action(MyAction));
            //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
            //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });
            //Task对象的构造函数传入的是一个委托
            //task1.Start();
            //task2.Start();
            //task3.Start();
            //task4.Start();


            Console.WriteLine("-------00-------");

            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();



            Console.ReadLine();
        }
        static void MyAction()
        {
            Console.WriteLine("MyAction   " + Thread.CurrentThread.ManagedThreadId);
        }
        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //var name = GetReturnResult2();     // 不用  await  不等待   主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法)
            // 加上 await 当异步方法 等待
            var name = await GetReturnResult2();     // 等待 异步方法里面 线程执行完  返回 string
            //加上 await 返回 string 不加Task<TResult>  
            // 不加 普通方法
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }
        static async Task<string> GetReturnResult2()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //return await Task.Run(() =>     // await只限于  当前异步方法等待            
            //return "";
            return await Task.Run(() =>     // await只限于  当前异步方法等待 //错误    4    由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>”            // 这里 Task.Run 会直接创线程执行的       异步操作的Result 才是 值 返回值     这里返回Task<string>
            {
                Thread.Sleep(5000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            //Thread.Sleep(2000);
            Thread.Sleep(10000);
            return "我是返回值";
        }
        //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。
        // await 会等待   线程执行完成  
        // Task.Run 和主线程一起执行  但是如果有返回值 主线程会卡住 等计算完 返回值
    }
}

 

 

 

  task 操作任务会开启子线程 执行任务

task.run 不会改变主流程线程
就不改变线程
我的手机  20:20:32
那就是 不等待 
 
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

Excute3();

public static async void Excute3()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
await SingleNoAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
}

await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行 

 

public static async Task SingleNoAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
//await 不等待

//.GetAwaiter().GetResult();   //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。   把这句拿掉 会创子线程一起 执行  // await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行     加上后 就等待 子线程 执行完 主线程才继续执行
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
});//.GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
return;
}

 

 

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

等10秒

 C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Threading;

namespace msdntask
{
    class Program
    {
        //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
        static void Main(string[] args)
        {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。    //GetAwaiter().GetResult(); 主线程等待子线程10S 主线程继续执行    不加主线程和子线程一起执行
            //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。
            //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕  这段太绝对 跟主线程执行到哪  子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
            //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
            //应该是 子线程值 阻塞主线程     因为主线程要得到返回值,必须要等方法执行完成。     Task.Run 会开子线程一起执行 


            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");

            //用法一
            Task task1 = new Task(new Action(MyAction));
            //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
            //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });
            //Task对象的构造函数传入的是一个委托
            //task1.Start();
            //task2.Start();
            //task3.Start();
            //task4.Start();


            Console.WriteLine("-------00-------");

            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();



            Console.ReadLine();
        }
        static void MyAction()
        {
            Console.WriteLine("MyAction   " + Thread.CurrentThread.ManagedThreadId);
        }
        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //var name = GetReturnResult2();     // 不用  await  不等待   主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法)
            // 加上 await 当异步方法 等待
            var name = await GetReturnResult2();     // 等待 异步方法里面 线程执行完  返回 string
            //加上 await 返回 string 不加Task<TResult>  
            // 不加 普通方法
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
        }
        static async Task<string> GetReturnResult2()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //return await Task.Run(() =>     // await只限于  当前异步方法等待            
            //return "";
            return await Task.Run(() =>     // await只限于  当前异步方法等待 //错误    4    由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>”            // 这里 Task.Run 会直接创线程执行的       异步操作的Result 才是 值 返回值     这里返回Task<string>
            {
                Thread.Sleep(5000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId + "  shijian" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //Thread.Sleep(2000);
            Thread.Sleep(10000);
            return "我是返回值";
        }
        //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。
        // await 会等待   线程执行完成  
        // Task.Run 和主线程一起执行  但是如果有返回值 主线程会卡住 等计算完 返回值
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();   //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。   把这句拿掉 会创子线程一起 执行   await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行     加上后 就等待 子线程 执行完 主线程继续执行

            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace async异步_Task分派任务_await等待
{
    public class Test
    {
        public async Task TestAsync()
        {
            await GetAsync();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            
        }
        public async Task GetAsync()
        {
            await Task.Delay(1);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }
    }

    class Program
    {
        //使用 async 关键字定义的异步方法简称为“异步方法”。
        //如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
        //async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
        //不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
        //一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
        //异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
        //        方法签名包含 async 修饰符。
        //按照约定,异步方法的名称以“Async”后缀结尾。
        //返回类型为下列类型之一:
        //如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
        //如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
        //void:如果要编写异步事件处理程序。
        //包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
        //方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
        static void Main(string[] args)
        {
            Test t = new Test();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            t.TestAsync();
            Console.ReadLine();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace await与async的正确打开方式
{
    class Program
    {
        static void Main(string[] args)
        {
            //await 只能在标记了async的函数内使用。
            //await 等待的函数必须标记async。
            //【很简单,await等待的是线程,不是函数。】
            Console.WriteLine(Thread.CurrentThread.GetHashCode());
            NoAsyncTest();
            AsyncTest();
            //await AsyncTest();
            Console.WriteLine();
            Excute();
            Console.ReadKey();
        }
        public static int NoAsyncTest()
        {
            return 1;
        }
        public static async Task<int> AsyncTest()//警告    7    此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。
        {// async Task<int>等于int
            //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
            //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
            //当然不是,那什么时候会让 await AsyncTest()有意义呢?
            Task.Run(() =>
            {//线程变化了 创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        public static async void Excute()
        {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await AsyncTest();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }


    }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。

}

 

前言:前面有篇从应用层面上面介绍了下多线程的几种用法,有博友就说到了async, await等新语法。确实,没有异步的多线程是单调的、乏味的,async和await是出现在C#5.0之后,它的出现给了异步并行变成带来了很大的方便。异步编程涉及到的东西还是比较多,本篇还是先介绍下async和await的原理及简单实现。

C#基础系列目录:

 

之前的那篇 C#基础系列——多线程的常见用法详解 就讲到了多线程new Thread()的方式对于有返回值类型的委托是没有解决方案的,如果需要返回值,必须要依靠异步的方式。了解异步之前,我们先来看看Thread对象的升级版本Task对象:

1、Task对象的前世今生:Task对象是.Net Framework 4.0之后出现的异步编程的一个重要对象。在一定程度上来说,Task对象可以理解Thread对象的一个升级产品。既然是升级产品,那它肯定有他的优势,比如我们上面Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static void Main(string[] args)
        {
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); });//启动Task执行方法
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine(strRes.Result);//得到方法的返回值
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            Console.ReadLine();
        }

        static string GetReturnResult()
        {
            Thread.Sleep(2000);
            return "我是返回值";
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

先来看结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static void Main(string[] args)
        {
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); });
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes.Result);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));

            Console.ReadLine();
        }

        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(2000);
            return "我是返回值";
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。

Task对象的用法如下:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
            //用法一
            Task task1 = new Task(new Action(MyAction));
          //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
        //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });

            task1.Start();
            task2.Start();
            task3.Start();
            task4.Start();
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由上可知,Task对象的构造函数传入的是一个委托,既然能传入Action类型的委托,可想而知Action的16中类型的参数又可以派上用场了。于是乎Task对象参数的传递就不用多说了吧。详见 C#基础系列——委托和设计模式(一)里面Action委托的用法。

 

2、初识 async & await。

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
        static void Main(string[] args)
        {
            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();
        }

        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var name = GetReturnResult();
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }

        static async Task<string> GetReturnResult()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            return await Task.Run(() =>
            {
                Thread.Sleep(3000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

结果:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

我们来看看程序的执行过程:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

由上面的结果可以得到如下结论:

(1)在async标识的方法体里面,如果没有await关键字的出现,那么这种方法和调用普通的方法没什么区别。

(2)在async标识的方法体里面,在await关键字出现之前,还是主线程顺序调用的,直到await关键字的出现才会出现线程阻塞。

(3)await关键字可以理解为等待方法执行完毕,除了可以标记有async关键字的方法外,还能标记Task对象,表示等待该线程执行完毕。所以await关键字并不是针对于async的方法,而是针对async方法所返回给我们的Task。

(4)是否async关键字只能标识返回Task对象的方法呢。我们来试试:

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

异步方法的返回类型必须为void、Task或者Task<T>类型。也就是说async要么是void,要么和Task关联。

 

3、除了await关键字,Task对象还有另外一种方式等待执行结果。  

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
     static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var name = GetReturnResult();
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name.GetAwaiter().GetResult(), DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

这样可以得到相同的结果。

name.GetAwaiter()这个方法得到的是一个TaskAwaiter对象,这个对象表示等待完成的异步任务的对象,并提供结果的参数。所以除了能完成await关键字的等待之外,它还能做一些其他的操作。我们将TaskAwaiter转到定义

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await
public struct TaskAwaiter<TResult> : ICriticalNotifyCompletion, INotifyCompletion
    {
        public bool IsCompleted { get; }
        public TResult GetResult();
        public void OnCompleted(Action continuation);
        public void UnsafeOnCompleted(Action continuation);
   }    
C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

IsCompleted:获取一个值,该值指示异步任务是否已完成。

GetResult():得到执行的结果。这个方法和await关键字效果相同。

OnCompleted():传入一个委托,在任务执行完成之后执行。

UnsafeOnCompleted():计划与此 awaiter 相关异步任务的延续操作。

由此可以看出,await关键字实际上就是调用了TaskAwaiter对象的GetResult()方法。

 

 


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;


namespace async异步_Task分派任务_await等待
{
public class Test
{
public async Task TestAsync()
{
await GetAsync();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);

}
public async Task GetAsync()
{
await Task.Delay(1);
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
}
}


class Program
{
//使用 async 关键字定义的异步方法简称为“异步方法”。
//如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
//async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
//不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
//一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
//异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
// 方法签名包含 async 修饰符。
//按照约定,异步方法的名称以“Async”后缀结尾。
//返回类型为下列类型之一:
//如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
//如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
//void:如果要编写异步事件处理程序。
//包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
//方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
static void Main(string[] args)
{
Test t = new Test();
Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
t.TestAsync();
Console.ReadLine();
}
}
}

 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace await与async的正确打开方式
{
    class Program
    {
        static void Main(string[] args)
        {
            //await 只能在标记了async的函数内使用。
            //await 等待的函数必须标记async。
            //【很简单,await等待的是线程,不是函数。】
            Console.WriteLine(Thread.CurrentThread.GetHashCode());
            NoAsyncTest();
            AsyncTest();
            //await AsyncTest();
            Console.WriteLine();
            Excute();
            Console.ReadKey();
        }
        public static int NoAsyncTest()
        {
            return 1;
        }
        public static async Task<int> AsyncTest()//警告    7    此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。
        {// async Task<int>等于int
            //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
            //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
            //当然不是,那什么时候会让 await AsyncTest()有意义呢?
            Task.Run(() =>
            {//线程变化了 创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        public static async void Excute()
        {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await AsyncTest();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }


    }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。

}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            Excute2();
            //Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult(); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Threading;

namespace msdntask
{
    class Program
    {
        //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
        static void Main(string[] args)
        {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行

            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。
            //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。
            //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes.Result);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕  这段太绝对 跟主线程执行到哪  子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
            //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
            //应该是 子线程值 阻塞主线程     因为主线程要得到返回值,必须要等方法执行完成。     Task.Run 会开子线程一起执行 


            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");

            //用法一
            Task task1 = new Task(new Action(MyAction));
            //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
            //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });
            //Task对象的构造函数传入的是一个委托
            //task1.Start();
            //task2.Start();
            //task3.Start();
            //task4.Start();


            Console.WriteLine("-------00-------");

            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();



            Console.ReadLine();
        }
        static void MyAction()
        {
            Console.WriteLine("MyAction   " + Thread.CurrentThread.ManagedThreadId);
        }
        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //var name = GetReturnResult2();     // 不用  await  不等待   主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法)
            // 加上 await 当异步方法 等待
            var name = await GetReturnResult2();     // 等待 异步方法里面 线程执行完  返回 string
            //加上 await 返回 string 不加Task<TResult>  
            // 不加 普通方法
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
        }
        static async Task<string> GetReturnResult2()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //return await Task.Run(() =>     // await只限于  当前异步方法等待            
            //return "";
            return await Task.Run(() =>     // await只限于  当前异步方法等待 //错误    4    由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>”            // 这里 Task.Run 会直接创线程执行的       异步操作的Result 才是 值 返回值     这里返回Task<string>
            {
                Thread.Sleep(5000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            //Thread.Sleep(2000);
            Thread.Sleep(10000);
            return "我是返回值";
        }
        //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。
        // await 会等待   线程执行完成  
        // Task.Run 和主线程一起执行  但是如果有返回值 主线程会卡住 等计算完 返回值
    }
}

 

 

 

  task 操作任务会开启子线程 执行任务

task.run 不会改变主流程线程
就不改变线程
我的手机  20:20:32
那就是 不等待 
 
 
 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

Excute3();

public static async void Excute3()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
await SingleNoAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
}

await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行 

 

public static async Task SingleNoAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}); //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
//await 不等待

//.GetAwaiter().GetResult();   //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。   把这句拿掉 会创子线程一起 执行  // await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行     加上后 就等待 子线程 执行完 主线程才继续执行
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
});//.GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
return;
}

 

 

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

等10秒

 C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

 

C#语法——await与async的正确打开方式  C#基础系列——异步编程初探:async和await
    


            
C#语法——await与async的正确打开方式
C#基础系列——异步编程初探:async和await

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using System.Threading;

namespace msdntask
{
    class Program
    {
        //Thread对象不能解决的问题:对于有返回值类型的委托。Task对象就能简单的解决。
        static void Main(string[] args)
        {//Task类表示不返回值且通常异步执行的单个操作 Task对象执行的工作通常在线程池线程上异步执行 而不是在主应用程序线程上同步执行
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine("执行GetReturnResult方法前的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            var strRes = Task.Run<string>(() => { return GetReturnResult(); }); ////表示一个可以返回值的异步操作。    //GetAwaiter().GetResult(); 主线程等待子线程10S 主线程继续执行    不加主线程和子线程一起执行
            //将指定的工作排成队列在线程池上运行,并返回由 function 返回的 Task(TResult) 的代理。
            //Run 将在线程池上运行的指定工作排队,并返回 function 返回的任务的代理项。
            Console.WriteLine("执行GetReturnResult方法后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            Console.WriteLine("我是主线程,线程ID:" + Thread.CurrentThread.ManagedThreadId);
            Console.WriteLine(strRes);
            Console.WriteLine("得到结果后的时间:" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //从结果分析可知在执行var strRes = Task.Run<string>(() => { return GetReturnResult(); })这一句后,主线程并没有阻塞去执行GetReturnResult()方法,而是开启了另一个线程去执行GetReturnResult()方法。(直到执行strRes.Result这一句的时候主线程才会等待GetReturnResult()方法执行完毕  这段太绝对 跟主线程执行到哪  子线程才执行完 没关系)。为什么说是开启了另一个线程,我们通过线程ID可以看得更清楚:
            //由此可以得知,Task.Run<string>(()=>{}).Reslut 值是阻塞主线程的,因为主线程要得到返回值,必须要等方法执行完成。
            //应该是 子线程值 阻塞主线程     因为主线程要得到返回值,必须要等方法执行完成。     Task.Run 会开子线程一起执行 


            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");
            Console.WriteLine("---------------------");

            //用法一
            Task task1 = new Task(new Action(MyAction));
            //用法二
            Task task2 = new Task(delegate
            {
                MyAction();
            });
            //用法三
            Task task3 = new Task(() => MyAction());
            Task task4 = new Task(() =>
            {
                MyAction();
            });
            //Task对象的构造函数传入的是一个委托
            //task1.Start();
            //task2.Start();
            //task3.Start();
            //task4.Start();


            Console.WriteLine("-------00-------");

            Console.WriteLine("我是主线程,线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            TestAsync();
            Console.ReadLine();



            Console.ReadLine();
        }
        static void MyAction()
        {
            Console.WriteLine("MyAction   " + Thread.CurrentThread.ManagedThreadId);
        }
        static async Task TestAsync()
        {
            Console.WriteLine("调用GetReturnResult()之前,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //var name = GetReturnResult2();     // 不用  await  不等待   主线程 直接执行下面 然后回过头执行GetReturnResult2的 Task.Run (当普通方法)
            // 加上 await 当异步方法 等待
            var name = await GetReturnResult2();     // 等待 异步方法里面 线程执行完  返回 string
            //加上 await 返回 string 不加Task<TResult>  
            // 不加 普通方法
            Console.WriteLine("调用GetReturnResult()之后,线程ID:{0}。当前时间:{1}", Thread.CurrentThread.ManagedThreadId, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", await name, DateTime.Now.ToString("yyyy-MM-dd hh:MM:ss"));
            Console.WriteLine("得到GetReturnResult()方法的结果:{0}。当前时间:{1}", name, DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
        }
        static async Task<string> GetReturnResult2()
        {
            Console.WriteLine("执行Task.Run之前, 线程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //return await Task.Run(() =>     // await只限于  当前异步方法等待            
            //return "";
            return await Task.Run(() =>     // await只限于  当前异步方法等待 //错误    4    由于这是异步方法,因此返回表达式必须是“string”类型而不是“Task<string>”            // 这里 Task.Run 会直接创线程执行的       异步操作的Result 才是 值 返回值     这里返回Task<string>
            {
                Thread.Sleep(5000);
                Console.WriteLine("GetReturnResult()方法里面线程ID: {0}", Thread.CurrentThread.ManagedThreadId);
                return "我是返回值";
            });
        }
        static string GetReturnResult()
        {
            Console.WriteLine("我是GetReturnResult里面的线程,线程ID:" + Thread.CurrentThread.ManagedThreadId + "  shijian" + DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
            //Thread.Sleep(2000);
            Thread.Sleep(10000);
            return "我是返回值";
        }
        //await 操作符应用于异步方法中的任务,以便在方法的执行中插入暂停点,直到等待的任务完成。这项任务代表着正在进行的工作。
        // await 会等待   线程执行完成  
        // Task.Run 和主线程一起执行  但是如果有返回值 主线程会卡住 等计算完 返回值
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace 正确打开方式
{
    class Program
    {
        public static async void Excute()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            var waitTask = AsyncTestRun(); //表示一个可以返回值的异步操作。
            waitTask.Start();
            //int i = 0;
            int i = await waitTask;
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
            Console.ReadKey();
        }
        public static Task<int> AsyncTestRun()
        {
            Task<int> t = new Task<int>(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
                return 100;
            });
            return t;
        }

        public static async Task<int> AsyncTest()
        {
            Task.Run(() =>       //将在线程池上运行的指定工作排队,并返回该工作的任务句柄。
            {//创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        static void Main(string[] args)   //错误    1    “正确打开方式.Program.Main(string[])”: 入口点不能用“async”修饰符标记
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            //AsyncTest();   //正常调用不会创线程
            //AsyncTest();  //出现三行 说明 创建了 一个线程 、、出现四行 说明 创建了 两个个线程  五行
            //但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
            //Excute();

            Console.WriteLine("--");
            Console.WriteLine("--");

            //Excute2();
            Excute3();
            Console.ReadKey();
        }

        public static async void Excute2()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute2 " + DateTime.Now);
            await SingleAwait();    //等待线程结束 然后执行后面的
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute2 " + DateTime.Now);
        }

        public static async Task SingleAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
            await Task.Run(() =>   //await  等待线程完成
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(3000);
            });
            await Task.Run(() =>       //Await会对线程进行优化。     
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            });
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
            return;
        }

        public static async void Excute3()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute3 " + DateTime.Now);
            await SingleNoAwait();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute3 " + DateTime.Now);
        }
        public static async Task SingleNoAwait()
        {
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();   //用来获取线程返回值的。这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。   把这句拿掉 会创子线程一起 执行   await 虽然等待 只等待一个         要是里面有Task.Run  还是会创子线程一起执行     加上后 就等待 子线程 执行完 主线程继续执行

            //await 不等待
            Task.Run(() =>
            {
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
            return;
        }

    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace async异步_Task分派任务_await等待
{
    public class Test
    {
        public async Task TestAsync()
        {
            await GetAsync();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            
        }
        public async Task GetAsync()
        {
            await Task.Delay(1);
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
        }
    }

    class Program
    {
        //使用 async 关键字定义的异步方法简称为“异步方法”。
        //如果 async 关键字修改的方法不包含 await 表达式或语句,则该方法将同步执行。 编译器警告将通知你不包含 await 语句的任何异步方法,因为该情况可能表示存在错误。
        //async 关键字是上下文关键字,原因在于只有当它修饰方法、lambda 表达式或匿名方法时,它才是关键字。 在所有其他上下文中,都会将其解释为标识符。
        //不要用 void 作为 async 方法的返回类型! async 方法可以返回 void ,但是这仅限于编写事件处理程序。一个普通的 async 方法如果没有返回值,要返回Task ,而不是 void 。
        //一定要避免使用Task.Wait 或 Task<T>.Result 方法,因为它们会导致死锁。如果使用了 async ,最好就一直使用它。
        //异步方法的参数不能使用out,ref。out 或 ref 返回的数据应借用Task<TResult> 返回,可以使用元组或自定义数据结构。
        //        方法签名包含 async 修饰符。
        //按照约定,异步方法的名称以“Async”后缀结尾。
        //返回类型为下列类型之一:
        //如果你的方法有操作数为 TResult 类型的返回语句,则为 Task<TResult>。
        //如果你的方法没有返回语句或具有没有操作数的返回语句,则为 Task。
        //void:如果要编写异步事件处理程序。
        //包含 GetAwaiter 方法的其他任何类型(自 C# 7.0 起)。
        //方法通常包含至少一个 await 表达式,该表达式标记一个点,在该点上,直到等待的异步操作完成方法才能继续。 同时,将方法挂起,并且控制返回到方法的调用方。
        static void Main(string[] args)
        {
            Test t = new Test();
            Console.WriteLine(Thread.CurrentThread.ManagedThreadId);
            t.TestAsync();
            Console.ReadLine();
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace await与async的正确打开方式
{
    class Program
    {
        static void Main(string[] args)
        {
            //await 只能在标记了async的函数内使用。
            //await 等待的函数必须标记async。
            //【很简单,await等待的是线程,不是函数。】
            Console.WriteLine(Thread.CurrentThread.GetHashCode());
            NoAsyncTest();
            AsyncTest();
            //await AsyncTest();
            Console.WriteLine();
            Excute();
            Console.ReadKey();
        }
        public static int NoAsyncTest()
        {
            return 1;
        }
        public static async Task<int> AsyncTest()//警告    7    此异步方法缺少“await”运算符,将以同步方式运行。请考虑使用“await”运算符等待非阻止的 API 调用,或者使用“await Task.Run(...)”在后台线程上执行占用大量 CPU 的工作。
        {// async Task<int>等于int
            //这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
            //目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
            //当然不是,那什么时候会让 await AsyncTest()有意义呢?
            Task.Run(() =>
            {//线程变化了 创建了新线程
                Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
                Thread.Sleep(1000);
            }).GetAwaiter().GetResult();
            return 1;
        }
        public static async void Excute()
        {//Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
            await AsyncTest();
            Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
        }


    }// “await”运算符只能用于异步方法中。请考虑用“async”修饰符标记此方法,并将其返回类型更改为“Task”。

}

 

相关文章:

  • 2022-12-23
  • 2021-11-02
  • 2021-12-27
  • 2022-12-23
  • 2021-06-30
  • 2021-06-26
  • 2021-07-07
猜你喜欢
  • 2021-03-31
  • 2021-06-11
  • 2022-12-23
  • 2022-12-23
  • 2023-03-10
  • 2021-06-22
相关资源
相似解决方案