0

I trying get a string to set three label text asynchronously, Firstly I tried with this

private async void button1_Click(object sender, EventArgs e)
{
    label1.Text = await SetTextbox(3);
    label2.Text = await SetTextbox(2);
    label3.Text = await SetTextbox(1);
}

private async Task<string> SetTextbox(int delaySeconds)
{
    Thread.Sleep(delaySeconds * 1000);
    return "Done.";
}

As you can see I try to add some delay in every call to know what label text should be set first, but this code doesn't work, the GUI still freezing and the processes still being synchronous.

For now I make it work using Task.Run:

private async void button1_Click(object sender, EventArgs e)
{
     var proc1 = Task.Run(async () => { label1.Text = await SetTextbox(3); });
     var proc2 = Task.Run(async () => { label2.Text = await SetTextbox(2); });
     var proc3 = Task.Run(async () => { label3.Text = await SetTextbox(1); });

     await Task.WhenAll(proc1, proc2, proc3);
}

but something tells me that this is not right way to achieve this, Can you tell me what would be the best way to do this or this is good solution?

3
  • I would also advice the use of await Task.Delay(delaySeconds * 1000) instead of the Thread.Sleep. The first one is made for use with async the latter is for when you're using threads. Commented Apr 3, 2020 at 22:18
  • What if you just change Thread.Sleep(delaySeconds * 1000); to await Task.Delay(delaySeconds * 1000); Commented Apr 4, 2020 at 1:31
  • Thread.Sleep() can be (and should be) used to simulate synchronous work. Task.Delay() would be wrong in that case. Commented Apr 5, 2020 at 11:15

3 Answers 3

2

The SetTextbox method is not a well behaved asynchronous method. The expected behavior of an asynchronous method is to return a Task immediately. Blocking the caller with Thread.Sleep breaks this expectation.

Now if we are handed with a badly behaving asynchronous method, Task.Run is our friend. Important: the only thing that should be wrapped inside the Task.Run is the blocking asynchronous method. Any UI related code should stay outside:

private async void Button1_Click(object sender, EventArgs e)
{
     var task1 = Task.Run(async () => await BadBlockingMethodAsync(1));
     var task2 = Task.Run(async () => await BadBlockingMethodAsync(2));
     var task3 = Task.Run(async () => await BadBlockingMethodAsync(3));

     await Task.WhenAll(task1, task2, task3);

     label1.Text = await task1;
     label2.Text = await task2;
     label3.Text = await task3;
}

As a side note, SetTextbox is an improper name for an asynchronous method. According to the guidelines the method should have an Async suffix (SetTextboxAsync).

Sign up to request clarification or add additional context in comments.

7 Comments

SetTextbox is not async at all. Async (behaviour) does not come from the async keyword but from awaiting something that is async.
@Henk Holterman according to the official docs, asynchronous is a method that returns an awaitable type. The SetTextbox returns Task<string>, which is awaitable, so the method is asynchronous. "Asynchronous methods in TAP include the Async suffix after the operation name for methods that return awaitable types, such as Task, Task<TResult>, ValueTask, and ValueTask<TResult>." (citation).
It is awaitable, yes. But it is not really async. It is faking it, as per the compiler warning. And in the first sample everything runs on the main thread, synchronously and sequentially.
@HenkHolterman I agree that the method fakes to be asynchronous based on its implementation, but having a name that ignores the official naming recommendations doesn't make things any better.
Yes, when posting my answer I realized it should be called GenerateString[Async](int), it is not setting a TextBox at all. And it shouldn't.
|
1

Setting those 3 labels are not dependent operations right. In that case, you can run all the method parallel and get their result once all done like

private async Task button1_Click(object sender, EventArgs e)
{
     var t1 = SetTextbox(3);
     var t2 = SetTextbox(2);
     var t3 = SetTextbox(1);

     string[] data = await Task.WhenAll(new[] { t1, t2, t3 });

    label1.Text = data[0];
    label2.Text = data[1];
    label3.Text = data[2];

} 

Comments

0

Asynchronous does not mean that it will execute in parallel - which is what you need if you want UI to stop freezing. And that is why Task.Run works. Have a look at this question
Your Task.Run solution should be ok - it is one of C# intended ways of running something in parallel

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.