TPL异步模式-续:
1.异步的接口声明
interface ITest
{
Task<int> GetAsync();//不要标注async
}
class Test : ITest
{
public async Task<int> GetAsync()
{
return 3;
}
}
接口中不需要标注 async 但实现类中必须标注 async
2.TPL 异步深入:
有如下异步方法:
static Task<string> F1Async()
{
MessageBox.Show("F1 Start");
return Task.Run(() => {
System.Threading.Thread.Sleep(1000);
MessageBox.Show("F1 Run");
return "F1";
});
}
调用:
private async void button1_Click(object sender, EventArgs e)
{
string i1 = await F1Async();
MessageBox.Show("i1=" + i1);
}
Task.Run()一个用来把一个代码段包装为Task的方法Run中委托的代码体就是异步任务执行的逻辑,最后return返回值。
并不是到了await才执行Task异步任务,而是这里是一个“最终保障”
因此不是只有await执行后Task才会真正执行。
总结:
static Task<string> GetRuPengAsync()
{
这里可以return Task<string>类型的值,不能return “a”;
}
static async Task<string> GetRuPengAsync()
{
这里不可以return Task<string>类型的值,而可以能return “a”;
}
- 只要方法是Task< T >类型的返回值,都可以用await来等待调用获取返回值
- 如果一个返回Task< T >类型的方法被标记了async,那么只要方法内部直接return T这个类型的实例就可以。
- 一个返回Task类型的方法没有被标记了async,那么需要方法内部直接Task实例。
对于不能标记 async 关键字的方法的异步用法:
下面是一个控制台程序:
static void Main(string[] args)
{
HttpClient hc = new HttpClient();
Task<HttpResponseMessage> tackMsg = hc.GetAsync("http://www.baidu.com");
HttpResponseMessage msg = tackMsg.Result;//对于一个Task来讲读取他的Result属性,也是相当于等待任务结束然后获取返回值。
Task<string> taskRead = msg.Content.ReadAsStringAsync();
string html = taskRead.Result;
Console.WriteLine(html);
Console.ReadKey();
//F1Async().Result 注意有的上下文下会有死锁。注意:一般不要这样用
}
1,如果返回值就是一个立即可以随手可得的值,那么就用Task.FromResult()如果是一个需要休息一会的任务(比如下载失败则过5秒钟后重试。主线程不休息,和Thread.Sleep不一样),那么就用Task.Delay()。
2,Task.Factory.FromAsync()把IAsyncResult转换为Task,这样APM风格的api也可以用await来调用。
3,编写异步方法的简化写法。如果方法声明为async,那么可以直接return 具体的值,不再用创建Task,由编译器创建Task:如下面三种写法
static async Task<int> F1Async()
{
return 1;
}
static Task<int> F2Async()
{
return Task.FromResult(3);
}
static Task<int> F3Async()
{
return Task.Run(()=> {
return 1 + 3;
});
}
3.TPL异常处理:
1、TPL中,如果程序中出现异常,除非进行try…catch,否则有可能是感觉不到出了异常的。
2,TPL程序有时候还会抛出AggregateException,这通常发生在并行有多个任务执行的情况下。
因为多个并行的任务可能有多个有异常,因此会包装为AggregateException异常AggregateException的 InnerExceptions 属性可以获得多个异常对象信息。
ASP.NET MVC 与 TPL
异步方法不是提升性能,不会提高访问速度,而是提升服务器的“吞吐量”,也就是可以处理的并发请求数得到增加。
返回值改为Task即可,如果方法标记为async,连自己创建Task都省了:
示例:
public async Task<ActionResult> Index()
{
return View();
}
能用异步方法,就不要用同步方法,可以轻松的提升系统的吞吐量!
异步方法的常见误区:
1、 一定要async到底
一定要让async的传染性(调用异步方法要用await,用了await方法就要声明为async,调用我这个async方法的地方必须要await……)不要轻易直接调用Task的Wait、WaitAll等方法。等待一个用await,而不是task.Wait();等待多个用await Task.WhenAll(),而不是Task.WaitAll()。
2:这样的异步就没有意义:
public async Task<string> GetArticleContentByNoRigntWayAsync()
{
return await Task.Run(() =>
{
using (var client = new WebClient())
{
return client.DownloadString("http://www.rupeng.com");//变成同步了
}
});
}
用了异步之后就能用异步api就用异步api,只要await、WhenAll,不要task.Result、Wait()、WaitAll()。ADO.Net、IO、EF等都有异步的API。
using System.Data.Entity之后First 之类的也有异步方法