C#异步编程async await

为什么需要异步,异步对可能起阻止作用的活动(例如,应用程序访问 Web 时)至关重要。 对 Web 资源的访问有时很慢或会延迟。 如果此类活动在同步过程中受阻,则整个应用程序必须等待。 在异步过程中,应用程序可继续执行不依赖 Web 资源的其他工作,直至潜在阻止任务完成。

  本节将一步一步带领大家理解async和await。

  期间会有

  Hello World原理介绍异步会提高程序的运行速度吗async和awaitMVC中的异步Action,以及线程中常涉及到的线程安全信号量,以及微软提供的异步API

  推荐先看后顶,学的更快!

 

Hello World

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static  void  Main( string [] args)
{
     new  Thread(Test) { IsBackground =  false  }.Start();       //.Net 在1.0的时候,就已经提供最基本的API.
     ThreadPool.QueueUserWorkItem(o => Test());               //线程池中取空闲线程执行委托(方法)
     Task.Run((Action)Test);                                  //.Net 4.0以上可用
     Console.WriteLine( "Main Thread" );
     Console.ReadLine();
}
 
static  void  Test()
{
     Thread.Sleep(1000);
     Console.WriteLine( "Hello World" );
}

  

原理

  其实不管是Task,ThreadPool,本质最终都是Thread。只不过微软帮我们在简化线程控制的复杂度。

  线程池是CLR中事先定义好的一些线程。Task取的线程池,只不过在语法上,可以非常方便取返回值。

 

异步会提高程序的运行速度吗

  多线程会提高程序的效率,不会提高运行速度。

  这就好比这一个任务让前台花1个小时。前台完成10分钟的时候

  打电话给经理,让他安排一个人来干30分钟(new Thread()),他干剩下的20分钟。(创建线程,需要时间,内存资源)

  或者从旁边空闲的同事中(ThreadPool 或 Task),拉一个人过来干30分钟。他干剩下的20分钟。(需要的时间少,资源本来就存在)

  从上看出,异步会让一份任务时间变长。资源消耗更多。但是可以让前台(UI线程)空闲下来,听从领导(用户)指挥。

 

 

async和await只是一个标记

  首先看个Demo,

1
2
3
4
5
6
7
8
9
10
11
static  void  Main( string [] args)
{
     Task.Run(() =>                                           //异步开始执行
     {
         Thread.Sleep(1000);                                  //异步执行一些任务
         Console.WriteLine( "Hello World" );                    //异步执行完成标记
     });
     Thread.Sleep(1100);                                      //主线程在执行一些任务
     Console.WriteLine( "Main Thread" );                        //主线程完成标记
     Console.ReadLine();
}

  发现执行结果是:

  这个很正常。但是我们希望先执行主线程完成标记,不改动主线程和Task的任务情况下,如何处理?

 

使用await和async

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static  void  Main( string [] args)
{
     Say();                              //由于Main不能使用async标记
     Console.ReadLine();
}
private  async  static  void  Say()
{
     var  t = TestAsync();
     Thread.Sleep(1100);                                      //主线程在执行一些任务
     Console.WriteLine( "Main Thread" );                        //主线程完成标记
     Console.WriteLine(await t);                              //await 主线程等待取异步返回结果
}
static  async Task< string > TestAsync()
{
     return  await Task.Run(() =>
     {
         Thread.Sleep(1000);                                  //异步执行一些任务
         return  "Hello World" ;                                //异步执行完成标记
     });
}

  1.凡是使用await关键字的方法,都必须打上async标记。

  2.async表示方法内有异步方法,调用async方法,会立刻另起线程执行。

  3.await只是显示等待线程结束。await表示等待异步方法执行完,并取返回值。

 

 

MVC中的异步Action

  既然多线程不能提高运行速度,而且每次请求Asp.net程序都是发起一个新的线程,为什么还要用多线程让其“降速”?

  为了提高网站的吞吐量。

  在MVC中,如果采用异步Action,则会像下面情况执行。

  1.请求到达IIS,IIS应用程序池分配一个worker线程用来响应请求。

  2.worker线程,执行异步操作,调用CLR线程池线程处理。

  3.释放worker线程,响应其他请求。

  4.异步操作执行完,w3wp(应用程序池进程)再次分配一个worker线程继续响应。

  上述使用场景中,会获取两次worker 线程,这两次获取的线程可能相同,也可能会不同。如果有比较耗时的任务,非常建议把同步请求转换为异步。

 

线程安全和信号量

   先举个线程不安全的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static  void  Main( string [] args)
{
     Task.Run((Action)Test);
     Task.Run((Action)Test);
     Console.ReadLine();
}
 
private  static  void  Test()
{
     if  (!IsComplete)
     {
         //todo other
         Thread.Sleep(500);
         Console.WriteLine( "执行完成" );
         IsComplete =  true ;
     }
}
 
public  static  bool  IsComplete {  get set ; }

  上面的执行结果,这就是线程不安全。(多线程访问同一段代码 产生不确定结果。)

  

如何解决,涉及到线程锁的概念。线程锁会让多线程访问的时候,一次只允许一个线程进入。

线程锁例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private  static  readonly  object  lockObj =  new  object ();
         public  static  bool  IsComplete {  get set ; }
         static  void  Main( string [] args)
         {
             Task.Run((Action)Test);
             Task.Run((Action)Test);
             Console.ReadLine();
         }
 
         private  static  void  Test()
         {
             lock  (lockObj)                               //锁住的必须是引用类型。由于在静态方法中,则锁住静态引用类型。
             {
                 if  (!IsComplete)
                 {
                     //todo other
                     Thread.Sleep(500);
                     Console.WriteLine( "执行完成" );
                     IsComplete =  true ;
                 }
             }
         }

  

信号量

  线程锁的技术使一块代码只能一个线程进入。信号量的存在,则是让同一块代码指定多个线程进入。

信号量(SemaphoreSlim)例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static  readonly  SemaphoreSlim slim =  new  SemaphoreSlim(2);
         static  void  Main( string [] args)
         {
             for  ( int  i = 0; i < 5; i++)
             {
                 ThreadPool.QueueUserWorkItem(Test, i);
             }
             Console.ReadLine();
         }
 
         private  async  static  void  Test( object  i)
         {
             Console.WriteLine( "准备执行"  + i);
             await slim.WaitAsync();
             Console.WriteLine( "开始执行"  + i);
             //todo other
             await Task.Delay(1000);
             Console.WriteLine( "执行结束"  + i);
             slim.Release();
         }

上面执行结果

 

 

 

从 .NET Framework 4.5 和 Windows 运行时中列出的 API 包含支持异步编程的方法。

应用程序区域

包含异步方法的受支持的 API

Web 访问

HttpClientSyndicationClient

使用文件

StorageFileStreamWriterStreamReaderXmlReader

使用图像

MediaCaptureBitmapEncoderBitmapDecoder

WCF 编程

同步和异步操作

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值