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 :
DoSomeSyncStuff
- then
DoBigSyncStuff will be run asynchronously inside
DoBigStuffAsync
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 :
DoSomeSyncStuff
- then
DoBigSyncStuff will start running asynchronously inside
DoBigStuffAsync
ParentMethodAsync will not wait for bigStuffTask to complete and will run DoSomeSyncStuffBeforeEndOfBigStuff
bigStuffTask (or DoBigStuffAsync) may complete before or after
DoSomeSyncStuffBeforeEndOfBigStuff does
- 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 :
DoSomeSyncStuff
- then
DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
- then
bigStuff2Task will start running asynchronously inside DoBigStuff2Async
ParentMethodAsync will wait for bigStuffTask and bigStuff2Task to complete
- 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 :
DoSomeSyncStuff
- then
DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
- then
bigStuff2Task will start running asynchronously inside DoBigStuff2Async
- then
DoSomeFireAndForgetStuff will start running asynchronously
and your code will never care to know if it completed or not
afterwards
ParentMethodAsync will wait for bigStuffTask and bigStuff2Task to complete
- 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 !