简体   繁体   English

.net core 3控制器中的并发/任务问题

[英]Question on concurrency/tasks in .net core 3 controller

I have a question on tasking/blocking/async on a controller setup in .net core 3. I've got the main controller methods set up as async, but have a question about how to handle downstream async calls within it 我对.net核心3中的控制器设置上的任务/阻止/异步有疑问。我已经将主控制器方法设置为异步,但是对如何处理其中的下游异步调用有疑问。

I have code working, just wondering if it's done optimally. 我有代码在工作,只是想知道它是否以最佳方式完成。

Controller code (methods of interest to this discussion), _verseReader was injected, and is an interface, and startup.cs could set this up based on config to read from almost any storage (xml, sql, mysql, cosmos, mongo, etc)...the controller doesn't care what storage is being used. 控制器代码(此讨论感兴趣的方法),_verseReader被注入,并且是一个接口,startup.cs可以基于config进行设置,以读取几乎所有存储(xml,sql,mysql,cosmos,mongo等) ...控制器不在乎正在使用什么存储。 It's all async, so the actual "reading" is done as a task. 它们都是异步的,因此实际的“读取”作为一项任务完成。

...
[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 is NOT setup async (because this was already all async) from the controller. vs.GetVerses没有从控制器设置异步(因为这已经是异步的)。 it makes a call to ReadVerses, which does some processing, parsing and setup: 它调用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 looks like: 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;
        }

Now it's the ReadFromStorage method (of the injected class) which actually calls out to obtain the data from whatever storage source was setup in config (Xml, SQL, Mongo, etc). 现在,它是(已注入类的)ReadFromStorage方法,该方法实际上从配置中设置的任何存储源(Xml,SQL,Mongo等)中调用以获取数据。 Everything is done sync, because this was all already spawned in a Task by the controller. 一切都完成了同步,因为控制器已经在Task中产生了所有内容。 However, for CosmosDB, it MUST use async calls, so I'm forced to do (see the call to .Result!). 但是,对于CosmosDB,它必须使用异步调用,因此我被迫这样做(请参阅对.Result的调用)。 I must do this because ReadFromStorage is not designated async. 我必须这样做,因为未将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;
         }

I've read up on .Result and it seems blocking (of course) so I'm wondering if this is a bad setup. 我已经读过.Result,而且似乎阻塞了(当然),所以我想知道这是否是错误的设置。 It's all already within a spawned task by the main controller method. 通过主控制器方法,所有这些都已在生成的任务中。 If I were to try to use await here, I must make the ENTIRE calling chain async?!? 如果要在此处使用await,则必须使整个调用链异步?!? That's my question. 那是我的问题。 From what I understand, I'd have to make ReadFromStorage async, then ReadVerses also async, then even further GetVerses async. 据我了解,我必须使ReadFromStorage异步,然后ReadVerses也异步,然后进一步使GetVerses异步。 So I'm wondering what is best in this case. 所以我想知道在这种情况下最好的方法是什么。 Is .Result ok? .Result可以吗?

And is there some way of doing await's without making the ENTIRE calling stack async also (which would force tasks to be created at 4 different times)...which seems silly (and slow). 并且有某种方法可以在不使ENTIRE调用堆栈也异步的情况下进行等待(这将迫使任务在4个不同的时间创建)...这似乎很愚蠢(而且很慢)。

There's no error message, The code works fine as is... it's a question on optimization. 没有错误消息,代码按原样工作正常……这是关于优化的问题。 It seems very confusing that you'd have to make (change code) of all callers to be async, just to use await down in a call stack somewhere? 您似乎必须使所有调用方都(更改代码)异步,以仅在某个地方的调用堆栈中使用await,这似乎很令人困惑。 Or maybe I'm missing something. 也许我想念一些东西。 Thanks. 谢谢。

I've refactored all the code in to async, and it all makes sense now, and is more coherent (and correct). 我已经将所有代码重构为异步的,这一切现在都很有意义,并且更加一致(正确)。 I also added "Async" suffix to my class reader method calls, to conform to convention. 我还向类读取器方法调用中添加了“异步”后缀,以符合约定。

After reading for 4 hours now about Tasks and await/async, I better understand the differences between "tasks" and "threads" (they are not the same thing). 在阅读了有关任务和等待/异步的4个小时之后,我更好地了解了“任务”和“线程”之间的区别(它们是不同的)。 Coming from other languages, "Task" usually was used to mean "Start New Thread", so that was causing a lot of confusion, as I was under the impression that adding await/async on methods started new threads for each method called. 来自其他语言的“任务”通常用来表示“启动新线程”,这引起了很多混乱,因为我的印象是,在方法上添加等待/异步会为每个调用的方法启动新线程。 But that is not the case. 但事实并非如此。 New threads are only started when Task.Run is called explicitly, and typically only used for sending compute intensive routines to a background THREAD, for processing. 新线程仅在显式调用Task.Run时启动,并且通常仅用于将计算密集型例程发送到后台THREAD进行处理。 Maybe this helps others who are encountering .net async/await (and .net Tasks) for the first time. 也许这可以帮助第一次遇到.net异步/等待(和.net Tasks)的其他人。

[HttpGet("{translation}")]
        public async Task<ActionResult<List<VerseSet>>> GetVerses([FromRoute] string Translation, [FromQuery] string Verse, [FromQuery] bool IncludeVerseNumbers=true)
        {
            VerseService vs = new VerseService(_verseReader);
            List<VerseSet> vList = await vs.GetVerses(Translation, Verse, IncludeVerseNumbers);
            return vList == null ? (ActionResult)NotFound(vList) : Ok(vList);
        }
   public async Task<List<VerseSet>> GetVerses(string TranslationName, string VerseMatchSpec, bool IncludeVerseNumbers)
        {
            ...
            VerseSet v = await ReadVerses(TranslationName, s, IncludeVerseNumbers);
            ...
            return vList;
        }
private async Task<VerseSet> ReadVerses(String TranslationName, String VerseMatchSpec, bool IncludeVerseNumbers)
        {
            ....
            List<VerseInfo> theVerseInfo = await _verseReader.ReadFromStorageAsync(v.Translation, vs.bookname, vs.chapter, vs.v1, vs.v2);
            ...
        }

If I were to try to use await here, I must make the ENTIRE calling chain async?!? 如果要在此处使用await,则必须使整个调用链异步?!? That's my question. 那是我的问题。

Yes. 是。 You should go async all the way . 您应该一直保持async状态

Is .Result ok? .Result可以吗?

It's "ok" in the sense that it would work (ie, not cause a YSOD). 这是在意义上的“OK”,它将工作 (即不会引起YSOD)。 However, on ASP.NET, either Result or Task.Run will artificially limit your server's scalability . 但是,在ASP.NET上, ResultTask.Run都会人为地限制服务器的可伸缩性

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM