Link to home
Create AccountLog in
Avatar of David L. Hansen
David L. HansenFlag for United States of America

asked on

Async with multiple UI objects

So I've been playing with Async and have had some success in making an asynchronous app. My practice code implements this:
Await Task.Run(Sub()
                  LongOperation()
           End Sub)

Open in new window

 
This however, only allows the UI to receive focus when one other operation is underway (useful, but too limited for my use). I need to be able to have the UI receive focus while two other operations are underway. And it would be nice if these other operations could each influence the UI (form).

I've tried a couple other Async methods but when I do, the first and second operations always occur synchronously.

Can someone provide a simple example where Asyc kicks off multiple operations during runtime, and where the user still has control of the UI?
Avatar of MikeToole
MikeToole
Flag of United Kingdom of Great Britain and Northern Ireland image

The following calls an asynchronous method which issues a call-back when each of 5 tasks completes ...
void Main()
{
	List<Task> tasks = Enumerable.Range(1, 5).Select(i => Task.Run(() => Thread.Sleep(i * 250 + 50))).ToList();
	DoTheAsyncWork(tasks, WriteResult);
	Console.WriteLine("I'm not waiting for them ...");	
}

public void WriteResult(string Result)
{
	Console.WriteLine(Result);
}

public async Task DoTheAsyncWork(List<Task> tasks, Action<string> WriteResult)
{
	while (tasks.Count() > 0)
	{
		Task FirstFinished = await Task.WhenAny(tasks.ToArray());
		tasks.Remove(FirstFinished);
		WriteResult($"Wait ended because task {FirstFinished.Id} completed.");
	}
}

Open in new window

Avatar of David L. Hansen

ASKER

Mike,

I've taken your code and tried to run it as strait C# and also as converted VB. I think it's close but errors still exist. I've tried to find work-arounds but to no avail.

Here's the converted VB and its error message:
    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim tasks As List(Of Task) = Enumerable.Range(1, 5).[Select](Function(i) Task.Run(Function() Threading.Thread.Sleep(i * 250 + 50))).ToList()
        Await DoTheAsyncWork(tasks, AddressOf WriteResult)
        Console.WriteLine("I'm not waiting for them ...")
    End Sub

Open in new window

The sleep command won't compile, showing an "Expression does not produce a value" error.

Did your C# code compile?
Change Function to Sub in your lambda.

i.e.

...Task.Run(Sub() Threading.Thread...

Open in new window

So it compiles now and runs, although it behaves synchronously. One through 10 (I changed it to 10) report one after the other, then the "I'm not waiting for them.." message is displayed.

Here is the code:
Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

    End Sub


    Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Dim tasks As List(Of Task) = Enumerable.Range(1, 10).[Select](Function(i) Task.Run(Sub() Threading.Thread.Sleep(i * 250 + 50))).ToList()
        Await DoTheAsyncWork(tasks, AddressOf WriteResult)
        'Console.WriteLine("I'm not waiting for them ...")
        Me.Label1.Text = "I'm not waiting for them ..."
        Me.Label1.Refresh()
    End Sub

    Public Sub WriteResult(Result As String)
        Console.WriteLine(Result)
    End Sub

    Public Async Function DoTheAsyncWork(tasks As List(Of Task), WriteResult As Action(Of String)) As Task
        While tasks.Count() > 0
            Dim FirstFinished As Task = Await Task.WhenAny(tasks.ToArray())
            tasks.Remove(FirstFinished)
            'WriteResult("Wait ended because task {FirstFinished.Id} completed.")
            Me.Label2.Text = "Wait ended because task" & FirstFinished.Id.ToString & " completed."
            Me.Label2.Refresh()
        End While
    End Function
End Class

Open in new window


thx in advance.
ASKER CERTIFIED SOLUTION
Avatar of MikeToole
MikeToole
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
See answer
@MikeToole

You should really be using a thread-safe collection to store your tasks, since you are modifying that collection while threads are running.
@käµfm³d

My answer was aimed at the basic question.
Thread-safety is an extra  - very important - consideration

Mike
Can I impose on your kindness a bit longer (both of you)? I'm seeing some of what's going on here but not all. Even setting aside thread-safe practices, there are still a point of confusion.

The working code now has a warning from the compiler and I'm not sure why. Someone else already put the following question to some other experts which expresses my confusion:
The whole point of Async is that it's asynchronous. I don't want it to block, because I want to get back to the MainWorkOfApplicationIDontWantBlocked ASAP and let GetNameAsync do its thing in the background. However, calling it this way gives me a compiler warning on the GetNameAsync line:

Warning 1   Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

I'm perfectly aware that "execution of the current method continues before the call is completed". That's the point of asynchronous code, right?
One of the experts suggested that the warning (explained in the quote above) exists because most of the time  we should await the tasks to finish before proceeding. Doesn't that defeat the purpose?
SOLUTION
Link to home
membership
Create a free account to see this answer
Signing up is free and takes 30 seconds. No credit card required.
I guess that makes sense..we should at least track when the async work finishes.

So if I use a task variable to hold the resulting task returned from the Async function, the error message goes away. And I remade my app to track the task's status (ie. RanToCompletion, Faulted, WaitingForActivation, etc.).

This helps a lot. Thank you!