.NET 异步用法

1、异步作用

 1.1 作用

 释放主线程的占用,提高吞吐量,直接运行并不能提高速度,合适场景使用,当然全部都写异步也并没大问题,只是代码难提升了,建议合适场景使用,高并发,数据库请求时间过长等场景使用

 1.2 入门

  • async void 和 async Task的区别:

    • Task类型返回值有等待效果,既能保证线程安全又不会堵塞主线程,是推荐的用法。

    • void没有等待效果,需要考虑线程安全,例如使用 db.CopyNew() 来保证。

    • 对于表达式也有 Action 和 Func<Task>,其中 Action 等同于 void,Func<Task> 等同于 Task。

  • List.ForEach 是一个 Action,所以 async 不会等待。可以替换为 foreach(var item in list)。

2、如何使用异步方法

  SqlSugar中ToList是同步方法,那么ToListAsync这种是异步方法,通过Async来区别是同步还是异步方法

  2.1 返回类型只能是Task或者Task<类型>

  2.2 只要用到异步方法必须加上await

Public void  GetAll(){  异步方法  }//错误  (方法要有Task返回值)
Public async Task GetAll(){  异步方法  }//错误 (缺少await)
Public async void GetAll(){  await  异步方法  }//错误  (方法要有Task返回值)
Public async Task GetAll(){  await  异步方法  }//正确
Public async Task<T> GetAll(){  await  异步方法  return xxx; }//正确

//异步表达式需要看类型是不是Task否则禁止用异步  
Action  a= async ()=>  await 异步方法 //错误 (Action等于Void不是Task)
Func<Task>   a= async()=> await 异步方法 //正确 

Parallel.Foreach(Action action) //错误  不是异步委托 (Action等于Void不是Task)

List.Forech(Action action)  // 错误    不是异步委托 (Action等于Void不是Task)

public Task TestAsync()//正确  forach是C#语法不是函数所以和委托没关系
{ 
  foreach(var item in list)
  {
     await 异步方法
  }
}


public Task TestAsync()//错误用法
{ 
  Task.Run(()=>{
     异步方法
  })
}
public void TestAsync()//正确用法  唯一一种 返回值不是Task 异步用法
{ 
   Task.Run(async ()=>{
      //如果是 SqlSugarClinet需要new
      await异步方法
  })
}
public Task TestAsync()//正确用法 
{ 
  await Task.Run(async ()=>{
      await异步方法
  })
}

用例:

var data=await db.Queryable<Order>().FirstAsync();
var list= await db.Queryable<Order>().Where(it=>it.Id==1).ToListAsync();
 
//分页需要特别注意用法
RefAsync<int> total = 0;
var list=await Db.Queryable<Order>().ToPageListAsync(1, 2, total);

如果是异步方法必须全部加上Await , 如果漏掉不写就会出现偶发性错误

//正确用法 每个异步方法都用await进行串起来
public  async Task<List<Order>> Test()
{
    var db = GetInstance();
 
    await GetAsync();
 
    await db.Queryable<OrderItem>().ToListAsync();
 
    return await db.Queryable<Order>().ToListAsync();
}
 
//错误用法,异步中禁止用sleep
 
Thread.Sleep(1)
 
//正确用法
 
await Task.Delay(1) //只能写到异步方法后面
 
//需要注意的用法
await Task.WhenAll
 //看文档 https://www.donet5.com/Home/Doc?typeId=2349

3、异步Canceltoken

db.Ado.CancellationToken = token;
List<Order> list1 =await db.Queryable<Order>().ToListAsync();
List<Order> list2 =await db.Queryable<Order>().ToListAsync();
db.Ado.RemoveCancellationToken();

4、异步常见错误

The connection does not support MultipleActiveResultSets

 (1)、SqlSugarClient 替换成 SqlSugarScope ,因为SqlSugarScope 是线程安全对象代码容错率高

   具体用法: https://www.donet5.com/Home/Doc?typeId=1181

   (2)  、异步用法错引起的 ,排查没用Await调用的异步方法 ,正确用法看当前文档2

5、Task.WhenAll代替Parallel

Parallel.ForEach 只能使用同步方法,不能使用异步方法

//参数2是一个 Action 而不是Func<Task> 所以是不支持异步表达式的
public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);

 请使用Task.WhenAll替换Parallel 具体用法如下:

https://www.donet5.com/Home/Doc?typeId=2349

6、特殊场景解决方案

有些情况必须强一个新对象出来保证线程安全,SqlSugarScope也需要这样处理

6.1 不加Await用异步  

注意:没有await一定要加CopyNew (SqlSugarScope也要加)

因为db对象是不支持跨上下文使用,直接写异步不加await 就会出现在不上下文使用db

这个时候用 db.Copynew() 就能实现强制new一个新的SqlSugarClient

//方式1:2种方式都必须加CopyNew()
db.CopyNew().Queryable<T>().ToListAsync() //事务var mydb=db.CopyNew()
db.CopyNew().Queryable<T>().ToListAsync()

6.2未知的第三方组件Job

db.CopyNew().Queryable<T>().ToListAsync()

6.3 Task.WhenAll 或者Parallel 

db.CopyNew().Queryable<T>().ToListAsync()

就这几种情况必须要加db.CopyNew(),其他只要用法正确都OK

关闭
果糖网