2

If an asynchronous call is made within a using statement, and the result of the call is processed asynchronously (i.e. the method within which this happens is async and returns before the result is loaded and processed), can the using statement go out of scope? In other words, is it safe to do something like this:

async void LoadAndProcessStuff()
{
    using(var ctx = CreateDBContext()){
        someResource.LoadStuffsAsync().ForEachAsync(stuff => ctx.Stuffs.Add(stuff));
    }
}

or

async void LoadAndProcessStuff2()
{
    using(var ctx = CreateDBContext()){
        ctx.Stuffs.Select(stuff => someResource.LoadMoreStuffsAsync(stuff))
            .ForEachAsync(stuff => ctx.Stuffs2.AddRange(stuff.Result));
    }
}

Or could ctx be Disposed by the time the ForEachAsync is called and cause an exception?

Liam
  • 25,247
  • 27
  • 110
  • 174
sammax
  • 31
  • 3
  • 3
    No this isn’t safe. `ctx` could, and surely will, be disposed while the asynchronous operation is running. This is basically the same as returning a disposable object from inside a using block. – InBetween Jan 06 '18 at 11:43
  • It would be fine if you `await`ed – Crowcoder Jan 06 '18 at 11:45
  • if I await, that means the method will only return when the whole operation is finished, right? I want it to run in the background because a whole bunch of such operations will be happening. And since the resource.LoadStuff is most likely a network operation, waiting until it is finished before starting the next operation would waste a lot of time – sammax Jan 06 '18 at 11:54
  • 2
    @no. Await will return immediately a task and mark the rest of the method as the continuation when the task is finished. What you need to do here is launch all the asynchronous operations *and then* `await` . Read [this answer](https://stackoverflow.com/a/48090684/767890) for an example. – InBetween Jan 06 '18 at 11:57
  • @InBetween That's definitely good to know :D Can I await in each of my LoadStuff methods and then await those methods from wherever they are called? I would like to keep them all seperated in their own methods because there is a bunch of logic in each method that I skipped for the examples. Also, would I have to await the ForEachAsync call, or await the LoadStuffsAsync or both? – sammax Jan 06 '18 at 12:02
  • Possible duplicate of [Synchronous implementation of interface that returns Task](https://stackoverflow.com/questions/28197902/synchronous-implementation-of-interface-that-returns-task) – Liam May 14 '18 at 16:01

2 Answers2

1

In an async method, the "using" keyword should be safe. The compiler just rewrites it as a "finally" expression, which works like all other exception handling in async methods. Note that Dispose() should NOT block or be long running, it should just release resources - unless you want to cripple your server.

This is an easy test case for the safe case:

using System;
using System.Threading.Tasks;

namespace so {
    sealed class Garbage : IDisposable {
        public void Dispose() {
            Console.WriteLine("Disposing Garbage");
        }
    }

    // Garbage is safely Disposed() only after delay is done
    class Program {
        public static async Task Main() {
            using (Garbage g = new Garbage()) {
                Console.WriteLine("Delay Starting");
                await Task.Delay(1000);
                Console.WriteLine("Delay Finished");
            }
        }
    }
}

However, if you have a non-async method that is returning a Task instead of awaiting it, it may be unsafe. That is because synchronous methods are only called once. There is no coroutine or state machine generated by compiler, so Dispose() HAS to be called right away - potentially while the Task is still running.

public Task DoWorkAsync()
{
    using (var service = new Service())
    {
        var arg1 = ComputeArg();
        var arg2 = ComputeArg();

        // NOT SAFE - service will be disposed
        // There is a high-probability that this idiom results 
        // in an ObjectDisposedException
        return service.AwaitableMethodAsync(arg1, arg2);
    }
}

For ref: http://www.thebillwagner.com/Blog/Item/2017-05-03-ThecuriouscaseofasyncawaitandIDisposable

Karl Pickett
  • 335
  • 1
  • 9
-5

If you want to safely do async/parallel stuff (and background stuff), the best is to use Tasks, async/await and ConfigureAwait. And keep in mind that it's always better to run the stuff inside a using before the execution leaves the method, so you have to think and encapsulate your code accordingly.

Here are some examples of what you may want to do :

  • execute a long running task asynchronously
public async Task ParentMethodAsync() {
    DoSomeSyncStuff();
    await DoBigStuffAsync();
    DoSomeSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
    await Task.Run(() => {
        DoBigSyncStuff();
    });
}

With that code your execution will be :

  1. DoSomeSyncStuff
  2. then DoBigSyncStuff will be run asynchronously inside DoBigStuffAsync
  3. ParentMethodAsync will wait for this to complete before running DoSomeSyncStuffAfterAsyncBigStuff

  • execute a long running task asynchronously on background
public async Task ParentMethodAsync() {
    DoSomeSyncStuff();
    // original context/thread
    await DoBigStuffAsync();
    // same context/thread as original
    DoSomeSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
    // here you are on the original context/thread
    await Task.Run(() => {
        // this will run on a background thread if possible
        DoBigSyncStuff();
    }).ConfigureAwait(false);
    // here the context/thread will not be the same as original one
}

Here same running order and blocking points, but with ConfigureAwait(false) you specify that you don't care synchronizing on the original context. Note that ParentMethodAsync context/thread is not impacted


  • execute stuff asynchronously and continue stuff at the same time
public async Task ParentMethodAsync() {
    DoSomeSyncStuff();
    Task bigStuffTask = DoBigStuffAsync();
    DoSomeSyncStuffBeforeEndOfBigStuff();
    await bigStuffTask;
    DoAnotherSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
    await Task.Run(() => {
        DoBigSyncStuff();
    });
}

With that code your execution will be :

  1. DoSomeSyncStuff
  2. then DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
  3. ParentMethodAsync will not wait for bigStuffTask to complete and will run DoSomeSyncStuffBeforeEndOfBigStuff
  4. bigStuffTask (or DoBigStuffAsync) may complete before or after DoSomeSyncStuffBeforeEndOfBigStuff does
  5. the await bigStuffTask will force ParentMethodAsync to wait for bigStuffTask to complete before running DoAnotherSyncStuffAfterAsyncBigStuff

  • execute multiple stuff asynchronously
public async Task ParentMethodAsync() {
    DoSomeSyncStuff();
    Task bigStuffTask = DoBigStuffAsync();
    Task bigStuff2Task = DoBigStuff2Async();
    await Task.WhenAll(bigStuffTask, bigStuff2Task);
    DoAnotherSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
    await Task.Run(() => {
        DoBigSyncStuff();
    });
}

With that code your execution will be :

  1. DoSomeSyncStuff
  2. then DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
  3. then bigStuff2Task will start running asynchronously inside DoBigStuff2Async
  4. ParentMethodAsync will wait for bigStuffTask and bigStuff2Task to complete
  5. once both completed, DoAnotherSyncStuffAfterAsyncBigStuff will run (synchronously)

  • execute stuff and don't wait/care for it to complete (Fire-and-forget)
public async Task ParentMethodAsync() {
    DoSomeSyncStuff();
    Task bigStuffTask = DoBigStuffAsync();
    Task bigStuff2Task = DoBigStuff2Async();
    // this one is fired and forgotten
    DoFireAndForgetStuffAsync();
    await Task.WhenAll(bigStuffTask, bigStuff2Task);
    DoAnotherSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
    await Task.Run(() => {
        DoBigSyncStuff();
    });
}
public async void DoFireAndForgetStuffAsync() {
    Task.Run(() => {
        try {
            DoSomeFireAndForgetStuff();
        } catch (Exception e) {
            // handle your exception
        }
    });
}

With that code your execution will be :

  1. DoSomeSyncStuff
  2. then DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
  3. then bigStuff2Task will start running asynchronously inside DoBigStuff2Async
  4. then DoSomeFireAndForgetStuff will start running asynchronously and your code will never care to know if it completed or not afterwards
  5. ParentMethodAsync will wait for bigStuffTask and bigStuff2Task to complete
  6. once both completed, DoAnotherSyncStuffAfterAsyncBigStuff will run (synchronously)

Please note that async void method should be used wisely and should always have their own exception handling inside. Otherwise, if an exception is thrown, as you have no way to control when and what the execution context will be in, you may end up with unexpected and random crashes.


There are other stuff you could learn from there for example, of with youtube livecode (e.g. this one from Xamarin Evolve in which James Clancey explains the thread aspects through a simple xamarin app)

I hope it will help you achieve what you want to !

AdricoM
  • 592
  • 5
  • 10