Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best asynchronous while method

Tags:

I need to write some asynchronous code that essentially attempts to repeatedly talk to and initialise a database. Quite often the first attempt will fail hence the requirement for it to retry.

In days of old I would have used a pattern similar to:

void WaitForItToWork() {     bool succeeded = false;     while (!succeeded)     {         // do work         succeeded = outcome; // if it worked, mark as succeeded, else retry         Threading.Thread.Sleep(1000); // arbitrary sleep     } } 

I realise a lot of changes have been made recently to .NET with regards to async patterns so my question really is this the best method to use or is it worth while exploring the async stuff and if so how do I implement this pattern in async?

Update

Just to clarify, I want to spawn this work asynchronously so that the method which spawns it does not have to wait for it to finish as it will be spawned in the constructor of a service so the constructor must return instantly.

like image 949
Chris Avatar asked Sep 03 '13 08:09

Chris


People also ask

Can a while loop be async?

Just like the for loop, the JavaScript while loop can use async/await too.

Should all methods be async?

If a method has no async operations inside it there's no benefit in making it async . You should only have async methods where you have an async operation (I/O, DB, etc.). If your application has a lot of these I/O methods and they spread throughout your code base, that's not a bad thing.

Which is the recommended way to wait for an async method to complete?

GetAwaiter. GetResult pattern to actually convert async to sync. But if you do that byitself in WPF or another SynchronizationContext bound to a particular thread you deadlock. This code avoids deadlock by transferring the async operation to the threadpool which is not synchronized to a particular thread.

What is asynchronous programming example?

Here's an example: Data may take long a long time to submit to a database. With asynchronous programming, the user can move to another screen while the function continues to execute. When a photo is loaded and sent on Instagram, the user does not have to stay on the same screen waiting for the photo to finish loading.


1 Answers

You could refactor that fragment like this:

async Task<bool> WaitForItToWork() {     bool succeeded = false;     while (!succeeded)     {         // do work         succeeded = outcome; // if it worked, make as succeeded, else retry         await Task.Delay(1000); // arbitrary delay     }     return succeeded; } 

Apparently, the only benefit it would give you is more efficient use of thread pool, because it doesn't always take a whole thread to make the delay happen.

Depending on how you obtain outcome, there may be much more efficient ways to get this job done using async/await. Often you may have something like GetOutcomeAsync() which would make a web service, database or socket call asynchronously in a natural way, so you'd just do var outcome = await GetOutcomeAsync().

It's important to take into account that WaitForItToWork will be split into parts by compiler and the part from await line will be continued asynchronously. Here's perhaps the best explanation on how it's done internally. The thing is, usually at some point of your code you'd need to synchronize on the result of the async task. E.g.:

private void Form1_Load(object sender, EventArgs e) {     Task<bool> task = WaitForItToWork();     task.ContinueWith(_ => {         MessageBox.Show("WaitForItToWork done:" + task.Result.toString()); // true or false     }, TaskScheduler.FromCurrentSynchronizationContext()); } 

You could have simply done this:

private async void Form1_Load(object sender, EventArgs e) {     bool result = await WaitForItToWork();     MessageBox.Show("WaitForItToWork done:" + result.toString()); // true or false } 

That would however make Form1_Load an async method too.

[UPDATE]

Below is my attempt to to illustrate what async/await actually does in this case. I created two versions of the same logic, WaitForItToWorkAsync (using async/await) and WaitForItToWorkAsyncTap (using TAP pattern without async/await). The frist version is quite trivial, unlike the second one. Thus, while async/await is largely the compiler's syntactic sugar, it makes asynchronous code much easier to write and understand.

// fake outcome() method for testing bool outcome() { return new Random().Next(0, 99) > 50; }  // with async/await async Task<bool> WaitForItToWorkAsync() {     var succeeded = false;     while (!succeeded)     {         succeeded = outcome(); // if it worked, make as succeeded, else retry         await Task.Delay(1000);     }     return succeeded; }  // without async/await Task<bool> WaitForItToWorkAsyncTap() {     var context = TaskScheduler.FromCurrentSynchronizationContext();     var tcs = new TaskCompletionSource<bool>();     var succeeded = false;     Action closure = null;      closure = delegate     {         succeeded = outcome(); // if it worked, make as succeeded, else retry         Task.Delay(1000).ContinueWith(delegate         {             if (succeeded)                 tcs.SetResult(succeeded);             else                 closure();         }, context);     };      // start the task logic synchronously     // it could end synchronously too! (e.g, if we used 'Task.Delay(0)')     closure();      return tcs.Task; }  // start both tasks and handle the completion of each asynchronously private void StartWaitForItToWork() {     WaitForItToWorkAsync().ContinueWith((t) =>     {         MessageBox.Show("WaitForItToWorkAsync complete: " + t.Result.ToString());     }, TaskScheduler.FromCurrentSynchronizationContext());      WaitForItToWorkAsyncTap().ContinueWith((t) =>     {         MessageBox.Show("WaitForItToWorkAsyncTap complete: " + t.Result.ToString());     }, TaskScheduler.FromCurrentSynchronizationContext()); }  // await for each tasks (StartWaitForItToWorkAsync itself is async) private async Task StartWaitForItToWorkAsync() {     bool result = await WaitForItToWorkAsync();     MessageBox.Show("WaitForItToWorkAsync complete: " + result.ToString());      result = await WaitForItToWorkAsyncTap();     MessageBox.Show("WaitForItToWorkAsyncTap complete: " + result.ToString()); } 

A few words on threading. There is no additional threads explicitly created here. Internally, Task.Delay() implementation may use pool threads (I suspect they use Timer Queues), but in this particular example (a WinForms app), the continuation after await will happen on the same UI thread. In other execution environments (e.g. a console app), it might continue on a different thread. IMO, this article by Stephen Cleary is a must-read to understand async/await threading concepts.

like image 185
noseratio Avatar answered Sep 18 '22 16:09

noseratio