3

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. 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. 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. The problem is that my program is not responsive, it blocks the UI while I'm processing the info.

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. Maybe someone can provide a look as well

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:

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.

3
  • 2
    string s = reader.ReadToEnd(); -> string s = await reader.ReadToEndAsync(); Commented Apr 16, 2017 at 13:21
  • 1
    One initial observation. try to avoid async void unless it is an event handler. test12 should be updated to return Task and awaited in the event handler mainBtn_Click. When going async you need to try to go async all the way. avoid blocking callls
    – Nkosi
    Commented Apr 16, 2017 at 13:21
  • @PetSerAl thanks, that solved the problem. I guess the ui thread got stuck there, thanks once more
    – Ladybird
    Commented Apr 16, 2017 at 13:52

2 Answers 2

1

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.

Try to avoid async void unless it is an event handler. test12 should be updated to return Task and awaited in the event handler 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.

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

6
  • Ty for help, but i guess core problem was where PetSerAl i the reader was blocking it. Thanks for the tip, now it works now in both cases but the standard is to return a task even if an event is happening(i click on the button so i thought on my function its okay to make it a void)?. Another "offtopic" question if you have time, i started working with task/async 2 days ago but i have a feeling i'm progressing really slow, i got a feeling programming is not really for me seems like other people are getting this stuff a lot faster. Am i progressing to slow?
    – Ladybird
    Commented Apr 16, 2017 at 14:02
  • I still cannot award you points for the question but will do so when i get the required reputation
    – Ladybird
    Commented Apr 16, 2017 at 14:02
  • Check out this article if you have not done so already . msdn.microsoft.com/en-us/magazine/jj991977.aspx
    – Nkosi
    Commented Apr 16, 2017 at 14:04
  • @user3398990, So the general answer to your problem above is to avoid mixing blocking calls with async calls. calling the sream.ReadToEnd is an IO blocking call.
    – Nkosi
    Commented Apr 16, 2017 at 14:06
  • yeah i have checked it, its "easy" to understand the concepts but when i started to work its a bit of a different story... i guess i lack experience or i'm too dumb for this stuff. Its my first time working with this stuff as well so i don't know
    – Ladybird
    Commented Apr 16, 2017 at 14:11
0

Quite often people think that async-await means that multiple threads are processing your code at the same time. 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. Search somewhere in the middle for async-await.

Eric Lipperts compares async-await processing with a cook who has to wait for his water to boil. 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.

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. Async await will only help if there are a lot of times your thread would be waiting for other processes to complete

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. 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(); // while the textReader is waiting for the information to be available // you can do other things 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
  • There is one exception: the async event handler returns void instead of 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
  • 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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.