async/await does not work properly

2019-06-10 01:05发布

I have problem with async/await method of mine. I am new into async/await and seems I can't get it to work correctly.

The GUI freezes when I have the following.

private async void SetUpTextBox(string s)
{
    //This method is called in button event
    string textToSet = await GetTextA(s);
    read_Box.Text = textToSet;
}

private Task<string> GetTextA(string s)
{
    return Task.Run(() => GetText(s));
}

private string GetText(string s)
{
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s)))
    {
        textToReturn = sr.ReadToEnd();
    }
    return textToReturn;
}

I don't know what I am doing wrong. I know I am new into this and that's why I am here to learn (a.k.a don't judge!).

P.S When I tried to change

using (StreamReader sr = new StreamReader(File.OpenRead(s)))
{
    textToReturn = sr.ReadToEnd();
}

With simple Thread.Sleep(2500) method. The GUI doesn't freeze at all!

3条回答
祖国的老花朵
2楼-- · 2019-06-10 01:29

Create an async void (for exemple Wait) with Thread.Sleep(x) inside and call it with "await Wait(x) ;" inside an async void.

查看更多
我想做一个坏孩纸
3楼-- · 2019-06-10 01:40

Avoid async void except for async event handlers, plus you can use ReadToEndAsync and make code async all the way through.

private async Task SetUpTextBox(string s) {
    //This method is called in button event
    string textToSet = await GetTextAsync(s);
    read_Box.Text = textToSet;
}

private async Task<string> GetTextAsync(string s) {
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s))) {
        textToReturn = await sr.ReadToEndAsync();
    }
    return textToReturn;
}

An example of calling SetUpTextBox within the button click event handler, as implied by the comment from original post

public async void OnBtnClicked(object sender, EventArgs args) {
    try {
        //...

        await SetUpTextBox("some path");

        //..
    } catch {
        //... handle error
    }
}

You should also put a try/catch around everything with the an async void event handler, otherwise any exceptions will go unobserved.

Reference Async/Await - Best Practices in Asynchronous Programming

查看更多
时光不老,我们不散
4楼-- · 2019-06-10 01:49

First off, the things you should do, that you aren't:

  • Using ReadToEndAsync.
  • Avoiding Task.Run when the task is not CPU bound.
  • Avoiding async void when the method is not an event handler.
  • Having proper error handling.

Nkosi has done a decent job illustrating that.

What is left is explaining why Thread.Sleep(2500) does not block the UI.


First off, notice that Task.Run will be running the task using the ThreadPool.

You can verify that System.Threading.Thread.CurrentThread.IsThreadPoolThread is true inside of GetText (for example, you can set a break point and use inspections to check).

That means that the UI thread cannot be blocked inside of GetText, because the UI thread does not run GetText.


But why then Thread.Sleep(2500); yields a different result?

Hmmm... what is the difference between:

private string GetText(string s)
{
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s)))
    {
        textToReturn = sr.ReadToEnd();
    }
    return textToReturn;
}

And:

private string GetText(string s)
{
    string textToReturn = "Hello";
    Thread.Sleep(2500);
    return textToReturn;
}

Let me tell you:

  • The possible exceptions thrown. Since you are not handling them - and I assume you are using a debug build in a debugger - you would have noticed. So, I do not think this is the problem. Even if you weren't, the exceptions are not from the UI thread, would go unhandled, and crash the application (Edit: I tested).
  • The ammount of time it takes to execute. However, since the UI will not be blocked by this code - being it running in the ThreadPool - that is not it either.
  • The ammount of text returned.

When you have eliminated the impossible, whatever remains, however improbable, must be the truth

-- Sherlock Holmes

The UI is being stuck because you are returning too much text.


Let me put this way...

TOO MUCH TEXT RETURNED

private string GetText(string s)
{
    string textToReturn = "Hello";
    using (StreamReader sr = new StreamReader(File.OpenRead(s)))
    {
        textToReturn = sr.ReadToEnd();
    }
    return textToReturn;
}

A little text returned:

private string GetText(string s)
{
    string textToReturn = "Hello";
    Thread.Sleep(2500);
    return textToReturn;
}

The UI, is therefore being stuck at read_Box.Text = textToSet;, busy trying to render all the text that you got from the file. Or at least that is my hyphothesis.

查看更多
登录 后发表回答