简体   繁体   English

我无法理解等待/异步是如何工作的

[英]I cannot understand how exactly does await/async work

I started to look into Task, async/await concepts is c# and I'm having big problems understanding it, well at least i don't know how to implement it. 我开始研究Task,async / await概念是c#,我在理解它时遇到很大问题,至少我不知道如何实现它。 I started rewriting an older test program i had written before, but now instead of threading i want to use these new concepts. 我开始重写我之前编写的旧测试程序,但现在我没有使用线程,而是希望使用这些新概念。 Basically the layout is as it follows: 基本上布局如下:

I have a simple class where i download the HTML content of a web page. 我有一个简单的类,我下载网页的HTML内容。 I process that in another class where i basically just parse the page to my model. 我在另一个类中处理它,我基本上只是将页面解析为我的模型。 Later on i want to display that to my UI. 稍后我想将它显示到我的UI。 The problem is that my program is not responsive, it blocks the UI while I'm processing the info. 问题是我的程序没有响应,它在我处理信息时阻止了UI。

I started learning this 2 days ago, i have read a lot of stuff online, including MSDN and some blogs but yet I'm unable to figure it out. 我2天前开始学习这个,我在线阅读了很多东西,包括MSDN和一些博客,但我无法弄明白。 Maybe someone can provide a look as well 也许有人可以提供一个外观

HtmlDOwnloadCOde: HtmlDOwnloadCOde:

public async Task<string> GetMangaDescriptionPage(string detailUrl)
{
    WebClient client = new WebClient();

    Stream data = await client.OpenReadTaskAsync(detailUrl);
    StreamReader reader = new StreamReader(data);
    string s = reader.ReadToEnd();
    data.Dispose();
    reader.Dispose();
    data.Close();
    reader.Close();
    return s;     
}

My parse class code: 我的解析类代码:

public async  Task<MangaDetailsModel> ParseMangaDescriptionPage()
{
    ParseOneManga pom = new ParseOneManga();
    string t1 = await pom.GetMangaDescriptionPage(selectedManga.url);
    HtmlDocument htmlDoc = new HtmlDocument();
        htmlDoc.LoadHtml(t1);
        var divs = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
            x.Attributes["id"].Value.Contains("title")).ToArray();
        mangaDetails.mangaName = divs[0].Element("h1").InnerText;

        mangaDetails.description = divs[0].Descendants("p").Single().InnerText ?? "DSA";
        var tds = divs[0].Descendants("td");
        int info = 0;

    var chapters = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
        x.Attributes["id"].Value.Contains("chapters")).ToArray();
    var chapterUi = chapters[0].Descendants("ul").Where(x => x.Attributes.Contains("class") &&
    x.Attributes["class"].Value.Contains("chlist"));
    foreach (var li in chapterUi)
    {
        var liChapter = li.Descendants("li");
        foreach (var h3tag in liChapter)
        {
            var chapterH3 = h3tag.Descendants("a").ToArray();
            SingleManagFox chapterData = new SingleManagFox();
            chapterData.name = chapterH3[1].InnerHtml;
            chapterData.url = chapterH3[1].GetAttributeValue("href", "0");
            mangaDetails.chapters.Add(chapterData);
        }
    };

    return mangaDetails;
}

UI code: UI代码:

private async   void mainBtn_Click(object sender, RoutedEventArgs e)
{
    if (mangaList.SelectedItem != null)
    {
         test12((SingleManagFox)mangaList.SelectedItem);     
    }
}

private async void test12(SingleManagFox selectedManga)
{
    selectedManga = (SingleManagFox)mangaList.SelectedItem;
    MangaDetails mangaDetails = new MangaDetails(selectedManga);
    MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
    txtMangaArtist.Text = mdm.artisName;
    txtMangaAuthor.Text = mdm.authorName;
    chapterList.ItemsSource = mdm.chapters;
}    

Sorry if its trivial but i cannot figure it out myself. 对不起,如果它的微不足道,但我无法弄清楚自己。

When going async you need to try to go async all the way and avoid mixing blocking calls with async calls. 在进行异步时,您需要尝试一直异步并避免使用异步调用混合阻塞调用。

You are using async void in the event handler with no await . 您在没有await的事件处理程序中使用async void

Try to avoid async void unless it is an event handler. 除非是事件处理程序,否则尽量避免async void test12 should be updated to return Task and awaited in the event handler mainBtn_Click . 应该更新test12以返回Task并在事件处理程序mainBtn_Click

private async void mainBtn_Click(object sender, RoutedEventArgs e) {
    if (mangaList.SelectedItem != null) {
       await test12((SingleManagFox)mangaList.SelectedItem);
    }
}

private async Task test12(SingleManagFox selectedManga) {
    selectedManga = (SingleManagFox)mangaList.SelectedItem;
    MangaDetails mangaDetails = new MangaDetails(selectedManga);
    MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
    txtMangaArtist.Text = mdm.artisName;
    txtMangaAuthor.Text = mdm.authorName;
    chapterList.ItemsSource = mdm.chapters;
}  

Also consider updating the web call to use HttpClient if available. 还可以考虑更新Web调用以使用HttpClient如果可用)。

class ParseOneManga {
    public async Task<string> GetMangaDescriptionPageAsync(string detailUrl) {
        using (var client = new HttpClient()) {
            string s = await client.GetStringAsync(detailUrl);
            return s;                
        }
    }
}

Reference: - Async/Await - Best Practices in Asynchronous Programming 参考: - 异步/等待 - 异步编程的最佳实践

Quite often people think that async-await means that multiple threads are processing your code at the same time. 人们通常认为async-await意味着多个线程同时处理您的代码。 This is not the case, unless you explicitly start a different thread. 除非您明确启动其他线程,否则情况并非如此。

A good metaphore that helped me a lot explaining async-await is the restauran metaphor used in this interview with Eric Lippert . 一个很好的metaphore帮助我解释了async-await, 是在与Eric Lippert的访谈中使用的餐馆比喻。 Search somewhere in the middle for async-await. 在中间某处搜索async-await。

Eric Lipperts compares async-await processing with a cook who has to wait for his water to boil. Eric Lipperts将异步等待处理与一位不得不等待水沸腾的厨师进行比较。 Instead of waiting, he looks around if he can do other things instead. 他没有等待,而是环顾四周是否可以做其他事情。 When finished doing the other thing, he comes back to see if the water is boiling and starts processing the boiling water. 完成另一件事后,他回来看水是否沸腾并开始处理沸水。

The same is with your process. 您的流程也是如此。 There is only one thread busy (at a time). 只有一个线程忙(一次)。 This thread keeps processing until he has to await for something. 这个线程继续处理,直到他必须等待某事。 This something is usually a fairly long process that is processed without using your CPU core, like writing a file to disk, loading a web page, or querying information from an external database. 这通常是一个相当长的过程,在不使用CPU核心的情况下处理,例如将文件写入磁盘,加载网页或从外部数据库查询信息。

Your thread can only do one thing at a time. 你的线程一次只能做一件事。 So while it is busy calculating something, if can't react on operator input and your UI freezes, until the calculations are done. 因此,当它忙于计算某些东西时,如果无法对操作员输入作出反应并且您的UI冻结,则直到计算完成。 Async await will only help if there are a lot of times your thread would be waiting for other processes to complete Async await只有在您的线程很多次等待其他进程完成时才会有所帮助

If you call an async function, you are certain that somewhere in that function is an await. 如果调用异步函数,则可以确定该函数中的某个位置是等待的。 In fact, if you declare your function async, and your forget to await in it, your compiler will warn you. 实际上,如果你声明你的函数是异步的,并且忘了等待它,你的编译器会警告你。

When your call meets the await in the function, your thread goes up its call stack to see if it can do other things. 当你的调用遇到函数中的await时,你的线程会调高其调用堆栈以查看它是否可以执行其他操作。 If you are not awaiting, you can continue processing, until you have to await. 如果您没有等待,可以继续处理,直到您必须等待。 The thread goes up its call stack again to see if one of the callers is not awaiting etc. 线程再次上调其调用堆栈以查看其中一个调用者是否在等待等。

async Task ReadDataAsync() { // do some preparations using (TextReader textReader = ...) { var myReadTask = textReader.ReadToEndAsync(); async Task ReadDataAsync(){//做一些准备工作(TextReader textReader = ...){var myReadTask = textReader.ReadToEndAsync(); // while the textReader is waiting for the information to be available // you can do other things ProcessSomething(); //当textReader等待信息可用时//你可以做其他事情ProcessSomething();

        // after a while you really need the results from the read data,
        // so you await for it.
        string text = await MyReadTask;
        // after the await, the results from ReatToEnd are available
        Process(text);
        ...

There are some rules to follow: 有一些规则要遵循:

  • an async function should return Task instead of void and Task<TResult> instead of TResult 异步函数应返回Task而不是void和Task<TResult>而不是TResult
  • There is one exception: the async event handler returns void instead of Task . 有一个例外:异步事件处理程序返回void而不是Task
  • Inside your async function you should await somehow. 在您的异步功能中,您应该等待某种方式。 If you don't await, it is useless to declare your function async 如果你没有等待,声明你的函数异步是没用的
  • The result of await Task is void, and the result of await Task<TResult> is TResult await Task的结果为空, await Task<TResult>的结果为TResult
  • If you call an async function, see if you can do some processing instead of waiting for the results of the call 如果您调用异步函数,请查看是否可以执行某些处理而不是等待调用结果

Note that even if you call several async functions before awaiting for them, does not mean that several threads are running these functions synchronously. 请注意,即使在等待它们之前调用多个异步函数,也不意味着多个线程同步运行这些函数。 The statement after your first call to the async function is processed after the called function starts awaiting. 第一次调用异步函数后的语句在被调用函数开始等待后处理。

async Task DoSomethingAsync()
{
    var task1 = ReadAsync(...);
    // no await, so next statement processes as soon as ReadAsync starts awaiting
    DoSomeThingElse();

    var task2 = QueryAsync(...);
    // again no await

    // now I need results from bothtask1, or from task2:
    await Task.WhenAll(new Task[] {task1, task2});

    var result1 = Task1.Result;
    var result2 = Task2.Result;
    Process(result1, result2);
    ...

Usually all your async functionality is performed by the same context. 通常,所有异步功能都由相同的上下文执行。 In practice this means that you can program as if your program is single threaded. 在实践中,这意味着您可以编程,就好像您的程序是单线程的。 This makes the look of your program much easier. 这使您的程序外观更容易。

Another article that helped me a lot understanding async-await is Async-Await best practices written by the ever so helpful Stephen Cleary 另一篇帮助我理解async-await的文章是Async-Await最好的做法,由有用的Stephen Cleary撰写

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

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