Why does await not wait? - c#

This is my actual code:
async Task getData()
{
Thread.Sleep(5000);
Console.WriteLine("Step 1");
using (HttpClient api = new HttpClient())
await api.GetAsync("http://google.com/").ContinueWith(
(getTask) =>
Console.WriteLine(getTask.Result.StatusCode);
);
Console.WriteLine("Step 2");
}
private void button1_Click(object sender, EventArgs e)
{
Task<Task> task = new Task<Task>(getData);
task.Start();
task.Wait();
Console.WriteLine("Step 3");
}
I get the following output:
Step 1
Step 3
OK
Step 2
Why does Step 3 not come after Step 2?
How do I get it to work in sequence, ie to return to the caller of getData only until everything in getData is done?

You should also mark you event handler as async
private async Task button1_Click(object sender, EventArgs e)
and await instead of Wait:
private async Task button1_Click(object sender, EventArgs e)
{
await getData();
Console.WriteLine("Step 3");
}
When we use the async/await pattern, we should go this way until the first method initiated it. Otherwise, by calling explicitly Wait or Result, we may run into deadlock issues. Furthermore, calling explicitly those methods you block the executing thread. So you drop the main benefit of using async/await, which is not blocking the executing thread and run on a separate thread some code and once this is done resume the execution of your code from where you stopped.

To try and put things simply and clearly - async Task (and <T>) methods are, themselves, responsible for allocating a Task object which represents their completion.
If you find yourself working with async methods and allocating Task objects yourself (either directly, as here, or using static factory methods such as Task.Run), you've got to recognize that more than one Task object now exists. You have to be careful and recognize situations where (as here) you create Tasks that themselves now return Tasks.
It's far better, if possible, to embrace async all the way (as you modify each function to be async, you'll then tend to locate each call site and make that location async as well) and to stop allocating any Tasks yourself.

Related

What is a good alternative to await Task.Run executing an asyncronous lambda?

I'm in the process of upgrading old code using BackgroundWorker. Following a series of articles from Stephen Cleary on this matter, I think the ideal substitute in this case is Task.Run().
Mine is a Windows Forms application. When a Form is constructed, different background operations are started and their results are collected in different event handlers, as per the BackgroundWorker pattern.
Now, my new methods follow the async/await pattern. I have some concerns about calling Task.Run() on an async methods.
private async void FormEventHandler(object sender, EventArgs e)
{
Task.Run(async () => await AwaitableLongRunningTask_1());
Task.Run(async () => await AwaitableLongRunningTask_2());
//...
}
AwaitableLongRunningTask_1 and _2 needs to be async, since the calls they makes within the method body are to async methods.
I can't call them in a simple await succession:
private async void FormEventHandler(object sender, EventArgs e)
{
await AwaitableLongRunningTask_1();
await AwaitableLongRunningTask_2();
//...
}
since they need to be started in parallel and they're obviously long running tasks spanning several seconds.
So my question is, do I need to refactor AwaitableLongRunningTasks to be non-async void, changing their internal behaviour like suggested here in "How to call asynchronous method from synchronous method"? Are there better options I'm missing?
Use Task.WhenAll
private async void FormEventHandler(object sender, EventArgs e)
{
await Task.WhenAll(
AwaitableLongRunningTask_1(),
AwaitableLongRunningTask_2());
}

Why is my c# test app blocking on one async function, but not another?

I have a piece of equipment that I can interact with via the accompanying API library provided by the equipment vendor. Most of the activity involves connecting to it, opening/closing its door, and a few other tasks. I've wrapped most of the vendor's synchronous API function calls in Task.Run() to use in a winforms test app to avoid blocking the UI. This has mostly worked, except for the initial Connect call that will block unless I use ConfigureAwait(false) when creating the equipment proxy object. I am struggling to understand why this function, and so far only this function, is causing the application UI to hang until it completes, while others (like the one to close the door) do not.
The SomeProxy object is a part of the vendor's API that inherits from RealProxy, but I have no access to its inner workings.
Excerpts of control functions:
private async void buttonConnect_Click(object sender, EventArgs e)
{
await Client.ConnectAsync(address);
}
private async void buttonCloseDoor_Click(object sender, EventArgs e)
{
await Client.CloseDoorAsync();
}
Async functions:
public async Task ConnectAsync(string address)
{
//some stuff happens here
// this blocks
//PieceOfEquipment = await Task.Run(() => VendorAPI.SomeProxy.Create(address));
// this does not block
PieceOfEquipment = await Task.Run(() => VendorAPI.SomeProxy.Create(address)).ConfigureAwait(false);
// some more stuff happens after
}
public async Task CloseDoorAsync()
{
await Task.Run(() => PieceOfEquipment.CloseDoor()); // this does not block
}
I appreciate it may be hard to say why given the black-boxed API functionality, but I am hoping to deepen my understanding here.

Async action hangs/deadlocks, why? (not waiting for Result, nor ConfiguringAwait)

First, sorry for yet another "why my async action hangs" question but I believe this question is different enough.
Surveying dozens of similar questions, the problem of async action deadlock is either in locking yourself out (.Result), using limited resources or using library components incorrectly (web requests seems popular). In the following example, I cannot find any from above:
private async Task ExecuteAsync(Task<int> task)
{
// entering on current thread, that is the main UI thread
await task // execute "task" asynchronnously (on a different thread)
.ConfigureAwait(false); // when done, no need to return to main thread
MessageBox.Show("success"); // succes indicator
}
public MainWindow() //wpf window ctor
{
InitializeComponent();
this.Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var task = new Task<int>(() => 42); // make an Action wrapping sychronnous method
// fire and forget, never caring for .Result, disard even the task
var _ = ExecuteAsync(task).ConfigureAwait(false);
}
I have commented the sample with my best try on exaplaining how things (should) work, but something in my explanation must be wrong. Even though MainWindow ctor does not deadlock, the Action () => 42 is never executed and "success" message is not shown. After some debugging I managed to fix the sample (using Task.FromResult), but I am still not sure what is wrong with it as it is now and even more importantly why.
What is the error in my reasoning and why was the action never executed/finished?
You did not start the task! You only declared it. Simply awaiting it does not "fire" it.
private async Task ExecuteAsync(Task<int> task)
{
// at first do start the task
task.Start();
await task.ConfigureAwait(false);
MessageBox.Show("success");
}
Note that ConfigureAwait(false) does not guarantee that execution will be continued on a different thread. It only says that you don't need it to be resumed on the original thread. And resuming UI work (like MessageBox.Show()) on a non-UI thread is not recommended.
As NineBerry pointed out, if you want to wrap a synchronous method and let it run on a different thread, you should use Task.Run():
var task = Task.Run(() => YourSynchronousCall());

await keyword blocks main thread

So I have the following code
private async void button1_Click(object sender, EventArgs e)
{
await DoSomethingAsync();
MessageBox.Show("Test");
}
private async Task DoSomethingAsync()
{
for (int i = 0; i < 1000000000; i++)
{
int a = 5;
}; // simulate job
MessageBox.Show("DoSomethingAsync is done");
await DoSomething2Async();
}
private async Task DoSomething2Async()
{
for (int i = 0; i < 1000000000; i++)
{
int a = 5;
} // simulate job
MessageBox.Show("DoSomething2Async is done");
}
Until both MessageBoxes are shown the main thread is block (I mean the application itself is frozen). There is obviously something wrong with my code and I can't figure out what. I've never used async/await before. this is my first attempt.
EDIT:
Actually what i want to do is to start Execution of DoSomethingAsync asynchronously so that when button is clicked the MessageBox.Show("Test"); would execute even though the DoSomethingAsync is incomplete.
I think you misunderstand what async means. It doesn't mean that the method runs in another thread!!
An async method runs synchronously until the first await, then returns a Task to the caller (unless it's async void, then it returns nothing). When the task that is being awaited completes, execution resumes after the await, usually on the same thread (if it has a SynchronizationContext).
In your case, the Thread.Sleep is before the first await, so it's executed synchronously, before control is returned to the caller. But even if it was after the await, it would still block the UI thread, unless you specifically configured the the awaiter not to capture the synchronization context (using ConfigureAwait(false)).
Thread.Sleep is a blocking method. If you want an async equivalent, use await Task.Delay(3000), as suggested in Sriram Sakthivel's answer. It will return immediately, and resume after 3 seconds, without blocking the UI thread.
It's a common misconception that async is related to multithreading. It can be, but in many cases it's not. A new thread is not implicitly spawned just because the method is async; for a new thread to spawn, it has to be done explicitly at some point. If you specifically want the method to run on a different thread, use Task.Run.
You should note that methods marked with async will no longer behave async when it lacks await. You'd have got compiler warning about this.
Warning 1 This async method lacks 'await' operators and will run
synchronously. Consider using the 'await' operator to await
non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work
on a background thread.
You should give attention to those warnings.
Do it asynchronously. When I say asynchronous it is really asynchronous not synchronous job in another thread.
Use Task.Delay which is really asynchronous. Use Task.Run for some time consuming job which is CPU bound.
private async void button1_Click(object sender, EventArgs e)
{
await DoSomethingAsync();
}
private async Task DoSomethingAsync()
{
await Task.Delay(3000); // simulate job
MessageBox.Show("DoSomethingAsync is done");
await DoSomething2Async();
}
private async Task DoSomething2Async()
{
await Task.Delay(3000); // simulate job
MessageBox.Show("DoSomething2Async is done");
}
You're not doing anything asynchronously. Try:
await Task.Run(() => Thread.Sleep(3000))
When you await a method, what you are effectively doing is providing a callback, with the code that follows it as what is executed once it finishes (in previous incarnations of async you actually had to set this up yourself). If you don't await anything then the method isn't really an asynchronous one.
For further information, you could do worse than looking here:
http://msmvps.com/blogs/jon_skeet/archive/2011/05/08/eduasync-part-1-introduction.aspx
Following your edit; try this:
public void MyMessageBox()
{
var thread = new Thread(() =>
{
MessageBox.Show(...);
});
thread.Start();
}
Although, are you sure it's really a message box you want? I would have thought a status bar / progress bar update would fit better.
In the DoSomethingAsync Thread.Sleep is called before await and in DoSomething2Async no async task is done.

Return async result to an event

Suppose I had and API event, where I had to return a result.
The computation I have to do uses an async method, which creates a control (must be run on the UI thread).
private async void API_QueryControl(object sender, APIEventArgs e)
{
e.Result = await CreateControl();
}
private async Task<Control> CreateControl()
{
await Task.Delay(1000);
return new Panel();
}
Now, this is obviously not going to work, since API_QueryControl yields before I set the result. I have no control over the API, which means the event cannot be changed. How would I get the event to wait for the result?
You can block the current (UI) thread, but avoid a deadlock, by using ConfigureAwait(false). Like this:
e.Result = await CreateControl().ConfigureAwait(false);
ConfigureAwait(false) makes the continuation part of CreateControl - the part after await Task.Delay - run on the threadpool instead of the original UI thread.
I assume you have some reason for using async like this, instead of just writing a synchronous version of CreateControl. That would not only be simpler code, it would also use the original (UI) thread to do the work, instead of using a second thread while the UI thread waits for it.
Also note that you should always prevent exceptions from being thrown from async void methods - see the accepted answer here.

Categories