Following a suggestion from svick I created a little class whose purpose is to run Tasks sequentially, that is, it schedules them on the ThreadPool but it ensures they execute one after the other, in the order they were submitted. It looks like this:
class SequentialTaskRunner<TResult> where TResult : new() {
public Task<TResult> Run(Func<TResult> func) {
var result = RunInternal(func, m_previous);
m_previous = result;
return result;
}
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous) {
await previous;
return await Task.Run(func);
}
Task<TResult> m_previous = Task.FromResult(new TResult());
}
Now, the problem I have is that if func() throws an exception, then every subsequent invocation of Run() will also return that exception, and it's impossible to run new tasks. I've tried to change RunInternal like so:
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous) {
if (previous.Exception == null) {
await previous;
}
return await Task.Run(func);
}
But this does not work reliably; if tasks are submitted quickly, the failure of one can still cause several to return the same exception. I am confused as to why and would like an explanation. I'm just getting started with async/await btw.
The reason why your code doesn't work is because you're pretty much asking it to predict the future.
When a Task isn't completed yet, its Exception will always be null. This means that it's quite likely that your await previous will throw.
The simplest solution here is to use a general catch:
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous)
{
try
{
await previous;
}
catch {}
return await Task.Run(func);
}
If you want to hide that empty catch (because that's usually a bad practice), or if you do this often and want to avoid repetition, you can write a helper method for this:
public static async Task IgnoreException(this Task task)
{
try
{
await task;
}
catch {}
}
Usage:
async Task<TResult> RunInternal(Func<TResult> func, Task<TResult> previous)
{
await previous.IgnoreException();
return await Task.Run(func);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With