我有一个方法返回一个对象的List <>。此方法需要一段时间才能运行。
private List<MyObject> GetBigList()
{
... slow stuff
}
从4或5个来源调用此方法。所以,我想我会尝试使用async并等待在此列表构建时保持移动。我添加了这个方法:
public async Task<List<MyObject>> GetBigListAsync()
{
var resultsTask = GetBigList();
var resuls = await resultsTask;
return resuls;
}
但是,在这一行:
var resuls = await resultsTask;
我收到此错误:
List <MyObject>不包含GetAwaiter的定义,并且找不到接受类型为List <MyObject>的第一个参数的扩展方法'GetAwaiter'。
我错过了什么?
看来你是async-await的新手。真正帮助我理解async-await做什么的是Eric Lippert在this interview.中给出的餐厅类比在中间寻找异步等待。
在这里,他描述如果厨师必须等待某事,而不是什么也不做,他开始环顾四周,看看他是否可以在此期间做其他事情。
Async-await类似。而不是等待要读取的文件,要返回的数据库查询,要下载的网页,您的线程将在调用堆栈上查看是否有任何调用者没有等待并执行这些语句,直到他看到等待。一旦他看到await,线程再次向上调用堆栈以查看其中一个调用者是否在等待等等。在读取文件或查询完成等一段时间后,执行await之后的语句。
通常在阅读您的大清单时,您的线程将非常繁忙,而不是闲着等待。不确定订购另一个线程来完成这些工作会增加读取列表所需的时间。考虑测量两种方法。
使用async-await的一个原因是,即使它会延长读取大列表所需的时间,也会使调用者(用户界面?)保持响应。
要使您的函数异步,您应该执行以下操作:
TResult
返回Task<TResult>
而不是void
返回Task
;await
,做你需要做的其他有用的事情,并在你需要结果时await
任务;等待你需要的结果。
private async Task<List<MyObject>> GetBigListAsync()
{
var myTask = Task.Run( () => GetBigList());
// your thread is free to do other useful stuff right nw
DoOtherUsefulStuff();
// after a while you need the result, await for myTask:
List<MyObject> result = await myTask;
// you can now use the results of loading:
ProcessResult(result);
return result;
}
再一次:如果你在其他线程加载List时没有什么有用的东西(比如保持UI响应),不要这样做,或者至少衡量你是否更快。
其他帮助我理解异步等待的文章是 - Async await,由那些非常有帮助的Stephen Cleary,以及更高级的:Async-Wait best practices。
resultTask
只是从GetBigList()
返回的列表,所以在那里什么都不会发生异步。
你可以做的是通过使用Task.Run
将任务卸载到线程池上的单独线程并返回等待的Task
对象:
// Bad code
public Task<List<MyObject>> GetBigListAsync()
{
return Task.Run(() => GetBigList());
}
虽然上面的示例最符合您的尝试,但这不是最佳做法。尝试使GetBigList()
本质上是异步的,或者如果真的没有办法,请将关于在单独的线程上执行代码的决定留给调用代码,并且不要在实现F.e中隐藏它。如果调用代码已经运行异步,则没有理由再生成另一个线程。 This article更详细地描述了这一点。