【发布时间】:2020-01-16 15:06:07
【问题描述】:
我有一个关于 .net core 3 中控制器设置的任务/阻塞/异步的问题。我已将主控制器方法设置为异步,但对如何处理其中的下游异步调用有疑问
我有代码工作,只是想知道它是否以最佳方式完成。
控制器代码(本讨论感兴趣的方法),_verseReader 被注入,并且是一个接口,startup.cs 可以根据配置设置它以从几乎任何存储(xml、sql、mysql、cosmos、mongo)中读取等)...控制器不关心正在使用什么存储。这都是异步的,所以实际的“阅读”是作为一项任务完成的。
...
[HttpGet("{translation}")]
public async Task<ActionResult<List<VerseSet>>> GetVerses([FromRoute] string Translation, [FromQuery] string Verse, [FromQuery] bool IncludeVerseNumbers=true)
{
VerseService vs = new VerseService(_verseReader);
var backgroundTask = Task.Run(() => vs.GetVerses(Translation, Verse, IncludeVerseNumbers));
var vList = await backgroundTask;
return vList == null ? (ActionResult)NotFound(vList) : Ok(vList);
}
...
vs.GetVerses 不是从控制器设置异步(因为这已经是全部异步)。它调用 ReadVerses,后者进行一些处理、解析和设置:
public List<VerseSet> GetVerses(string TranslationName, string VerseMatchSpec, bool IncludeVerseNumbers)
{
List<VerseSet> vList = new List<VerseSet>();
...
VerseSet v = ReadVerses(TranslationName, s, IncludeVerseNumbers);
...
return vList;
}
ReadVerses 看起来像:
private VerseSet ReadVerses(String TranslationName, String VerseMatchSpec, bool IncludeVerseNumbers)
{
...some code here
List<VerseInfo> theVerseInfo = _verseReader.ReadFromStorage(v.Translation, vs.bookname, vs.chapter, vs.v1, vs.v2);
...some code here
return v;
}
现在是 ReadFromStorage 方法(注入类的),它实际上调用从配置中设置的任何存储源(Xml、SQL、Mongo 等)获取数据。一切都已同步完成,因为这一切都已由控制器在 Task 中生成。但是,对于 CosmosDB,它必须使用异步调用,所以我不得不这样做(参见对 .Result 的调用!)。我必须这样做,因为 ReadFromStorage 没有被指定为异步的。
public List<VerseInfo> ReadFromStorage(String Translation, String Bookspec, int Chapter, int StartingVerse, int EndingVerse)
{
...code here
var bookResult = client.CreateDocumentQuery<CosmosBook>(UriFactory.CreateDocumentCollectionUri(_connInfo["DatabaseId"], "books"), new FeedOptions { EnableCrossPartitionQuery = true , MaxItemCount = 1})
.Where(b => b.Matchspec.Contains($"[{Bookspec}]"))
.AsDocumentQuery()
.ExecuteNextAsync<CosmosBook>().Result;
...code here
return vList;
}
我已经阅读了 .Result 并且它似乎阻塞(当然)所以我想知道这是否是一个糟糕的设置。这一切都已经在主控制器方法的衍生任务中了。如果我在这里尝试使用 await,我必须使整个调用链异步?!?那是我的问题。据我了解,我必须使 ReadFromStorage 异步,然后 ReadVerses 也异步,然后再进一步 GetVerses 异步。所以我想知道在这种情况下什么是最好的。 .Result 好吗?
还有什么方法可以在不使整个调用堆栈异步的情况下进行等待(这将强制在 4 个不同的时间创建任务)...这看起来很愚蠢(而且很慢)。
没有错误信息,代码按原样工作正常......这是一个关于优化的问题。您必须使所有调用者的(更改代码)异步,只是为了在某个调用堆栈中使用 await down,这似乎很令人困惑?或者,也许我错过了一些东西。谢谢。
【问题讨论】:
-
我刚刚找到了 Stephen Cleary 的 very good article。希望它会有所帮助
-
好的,谢谢,我去看看。我想我的一般问题是:您如何处理存在您不想更改(或无法更改)的现有调用堆栈,但您发现现在需要添加(向下 N 个级别)的情况一些突然变成异步的新代码?
-
通常我会尝试创建此调用堆栈
async,但如果我别无选择,只能对异步函数进行同步调用,我可能会使用GetAwaiter().GetResult()而不是.Result,因为它传播异常而不是将它们包装在AggregateException中。但是我会提防可能的死锁 -
这似乎更适合Code Review,因为您的问题似乎符合on-topic list 中的大多数标准。