async/await is behaving weirdly by restarting already executed code - c#

I am seeing some weird behavior related to async/await, but I can't explain it.
Here is the basic logic:
public async Task Init(UI.INavigable NavigateFrom) <-- Starts from here
{
await NavigateFrom.Navigate<UI.Pages.Page>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
{
// ...
m_SelectPage.OnCategorySelected += OnCategorySelected;
// ... (including more awaits)
await OnModelUpdated();
return Page;
});
}
private async Task OnModelUpdated()
{
await m_SelectPage.DataUpdated(...);
}
public async Task DataUpdated(...)
{
try
{
m_Initializing = true;
await Common.RunUiTask(async () =>
{
// ... (including more awaits)
await Task.Run(async () =>
{
// ... (including more awaits)
System.Diagnostics.Debug.Print("DataUpdated: Calling CategorySelected\n");
await OnCategorySelected(await GetSelectedCategory());
});
});
}
finally
{
m_Initializing = false;
}
}
private async Task OnCategorySelected(Model.Category Category)
{
Debug.Print("OnCategorySelected start\n");
var Adaptor = new Model.Adaptor(...);
var p = new InfoPage(m_ActualApp, Adaptor);
Debug.Print("OnCategorySelected navigate\n");
await p.Init(SelectPage);
Debug.Print("OnCategorySelected end\n");
}
public async Task Init(UI.INavigable NavigateFrom)
{
Debug.Print("Init start\n");
await NavigateFrom.Navigate<UI.Pages.InfoPage>(await Common.CreateDrillInNavigationTransitionInfo(), async (Page) =>
{
Debug.Print("Init lambda\n");
// ...
Debug.Print("Init lambda update\n");
await m_InfoPage.Updated(...);
Debug.Print("Init lambda end\n");
return Page;
});
Debug.Print("Init end\n");
}
And here is the output from one run (it doesn't behave the same way every time):
DataUpdated: Calling CategorySelected
OnCategorySelected start
OnCategorySelected navigate
Init start
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda
Init lambda update
OnCategorySelected start
OnCategorySelected navigate
Init start
Init lambda end
Init end
OnCategorySelected end
Init lambda
Init lambda update
Init lambda
Init lambda update
Init lambda end
Init end
OnCategorySelected end
Init lambda end
Init end
OnCategorySelected end
As is clearly seen from the output, it seems that as soon as it hits await in the Init function, or it hits await inside the lambda in the Init function, it suspends (which is correct), but then restarts back in the OnCategorySelected function for no apparent reason.
I can't explain what's happening. It should just happen once since there is no code that calls OnCategorySelected again. Any ideas?
For reference, here are the utility functions that I use in case there's some weirdness in those:
public static async Task<DrillInNavigationTransitionInfo> CreateDrillInNavigationTransitionInfo()
{
return await Common.RunUiTask(async () =>
{
return await Task.FromResult(new DrillInNavigationTransitionInfo());
});
}
public static class Common
{
public static async Task<T> RunUiTask<T>(Func<Task<T>> F)
{
return await CoreApplication.MainView.CoreWindow.Dispatcher.RunTaskAsync(async () =>
{
return await F();
});
}
}
public static class DispatcherTaskExtensions
{
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, CoreDispatcherPriority Priority, Func<Task<T>> Func)
{
var TaskCompletionSource = new TaskCompletionSource<T>();
await Dispatcher.RunAsync(Priority, async () =>
{
try
{
var R = await Func();
TaskCompletionSource.SetResult(R);
}
catch (Exception ex)
{
TaskCompletionSource.SetException(ex);
}
});
return await TaskCompletionSource.Task;
}
public static async Task<T> RunTaskAsync<T>(this CoreDispatcher Dispatcher, Func<Task<T>> Func)
{
return await RunTaskAsync(Dispatcher, CoreDispatcherPriority.Normal, Func);
}
}

As i see, everything is OK. Async means asynchronous, if u don't know. You invoked OnCategorySelected 3 times and it finished 3 times (you can see that in ur log). If you need async but sometimes synchronous completion is necessary you can use SemaphoreSlim for that.
SemaphoreSlim sl = new SemaphoreSlim(1, 1);
//some async code
await sl.WaitAsync();
//some sync code
sl.Release();
//again async code

Related

Pass Func<Task> as parameter

I have a method:
public async Task Method(Func<Task> action)
This function basically checks if few seconds have passed since the last run time.
I tried:
Func<Task> timer = async () => { await Task.Delay(3000);};
and invoking it like that
Func<Task> check = await _className.Method(timer);
However it says
Cannot implicitly convert 'void' to 'System.Func<System.Threading.Tasks.Task>'
Basically I am not able to pass a delayed task to a function.
Here's a trivial example of calling Method with a Func<Task>.
Func<Task> timer = async () => { await Task.Delay(3000);};
await Method(timer);
async Task Method(Func<Task> action)
{
await action();
}
I'm not sure exactly what you're trying to do, but this code works:
public class Program
{
public static async Task Main(string[] args)
{
var sw = Stopwatch.StartNew();
Func<Task> timer = async () => { await Task.Delay(3000); };
var theTask = Method(timer);
await theTask;
Console.WriteLine(sw.ElapsedMilliseconds); // ~3000
}
public static async Task Method(Func<Task> action)
{
await action();
}
}
I just put in var theTask = Method(timer); to illustrate that you can assign the return value to a Task before awaiting it.
Okay, so it works like tymtam already said - it looks like my problem was trying to assign end result to Func, i guess i need more sleep.
Thank you for the help tho
EDIT
I needed this:
Func<Task> check = async () => await _className.Method(timer);

await inside a WPF Dispatcher call

What is the best solution to ensure errorhandling when awating inside a delegate that is pased to the dispatcher?
In short: I need to return a number after parsing a calculation to Foo. That calculation is very slow and have to be done on the UI thread. The calculation must be handed to another class as a Task<string>.
The only solution I could come up with was CallFooWithATask___3 - and not even sure about that..
public class Foo
{
public void CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(Task<string> task) { }
}
// CallFooWithATask___ is invoked from unknown thread. Can't wait for GetWithSideEffects on calling thread
public class SomeClass
{
private TextBox textBox;
public int CallFooWithATask___1(Foo foo)
{
// not good - async void -> no error handling
var tcs = new TaskCompletionSource<string>();
Dispatcher.CurrentDispatcher.BeginInvoke(async () =>
{
var a = await GetWithSideEffects();
textBox.Text = a;
tcs.SetResult(a);
});
// quite fast - probally put it in a queue and returns
foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(tcs.Task);
return 1;
}
public async Task<int> CallFooWithATask___2(Foo foo)
{
// not good - still async void -> no error handling .. when is foo actually called? I assume when hitting the inner await'ish?
var task = await Dispatcher.CurrentDispatcher.InvokeAsync(async () =>
{
var a = await GetWithSideEffects();
textBox.Text = a;
return a;
});
// quite fast - probally put it in a queue and returns
foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);
return 1;
}
public int CallFooWithATask___3(Foo foo)
{
// what is the elegant solution - probally not this?
var tcs = new TaskCompletionSource<string>();
Dispatcher.CurrentDispatcher.BeginInvoke(async () =>
{
try
{
var a = await GetWithSideEffects();
textBox.Text = a;
tcs.SetResult(a);
}
catch (Exception ex) { tcs.SetException(ex); }
});
// quite fast - probally put it in a queue and returns
foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(tcs.Task);
return 1;
}
// this might trigger ui updates and is very slow ..
private Task<string> GetWithSideEffects()=> Task.FromResult("42");
}
You're pretty close. Just extract your async code in a method or in a Func<Task<string>> to avoid ending with an async void:
Func<Task<string>> func = async () =>
{
var a = await GetWithSideEffects();
return a;
};
Then invoke it with InvokeAsync. You'll end up with a Task<Task<string>>. The inner task is the one returned by your async method, the outer task is the one generated by InvokeAsync to indicate when the call is actually dispatched.
Use .Unwrap to merge those tasks, and finally send them to your other method:
var task = Dispatcher.InvokeAsync(func).Task.Unwrap();
foo.CallMeWithATaskThatIsFinishedWhenTheUIIsUpdated(task);

Awaiting manually created task is freezing ASP.NET app

I've made a queue, which contains tasks to do. After creating some tasks manually with new Task() in Returns method, my whole application hangs - await current;. The body of the task is not even triggered.
ConfigureAwait(false) didn't help.
The first task in the queue, which is not created by me, but other framework is executing successfully and returning a value. Mine - doesn't. I've tried add Task.CompletedTask and then it has worked. I don't understand why I can't even reach the body of the task containing _output assignment.
IDE debugger code screenshot
---UPDATE---
The code works when I use code below. With await it doesn't. Any ideas?
current.Start();
current.Wait();
Original code
private readonly Queue<Task> _pipe;
public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
_pipe.Enqueue(new Task(() => // this task causes a problem and breakpoint isn't hit inside
{
_output = outputBuilder(_results);
}));
return this;
}
public async Task<TOutput> Execute()
{
Task current;
while (_pipe.TryDequeue(out current))
{
if (current.IsCommandExecution())
{
IExecutionResult result = await (Task<IExecutionResult>)current; // this awaits successfully
_results.Add(result);
}
else
{
await current; // hangs here
}
}
return await Task.FromResult(_output);
}
Usage
[HttpGet("eventflow/pipe/issue/add/{title}")]
public async Task<IActionResult> PipeAction(string title)
=> Ok(
await Pipe<IExecutionResult>()
.Validate(title)
.Handle<AddIssueCommand>(IssueId.New, title)
.Returns(results => results.First())
.Execute());
You should never use the Task constructor. This goes double on ASP.NET, since constructed tasks are always Delegate Tasks, which interfere with the ASP.NET usage of the thread pool. The actual reason that the await hangs is because manually-created tasks need to be started.
If you have synchronous work that you need to wrap into a Task to work alongside asynchronous tasks, then you should use Task.CompletedTask and Task.FromException:
private static Task SynchronousWork(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
try { _output = outputBuilder(_results); return Task.CompletedTask; }
catch (Exception ex) { return Task.FromException(ex); }
}
public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
_pipe.Enqueue(SynchronousWork(outputBuilder));
return this;
}
However, note that this executes outputBuilder immediately, which may not be desirable due to its side effects on _results and _output. If you want a delayed execution queue, then the type in the queue needs to be changed from Task to Func<Task>. Then you can add to it as such:
public IPipeBuilder<TOutput> Returns(Func<IEnumerable<IExecutionResult>, TOutput> outputBuilder)
{
_pipe.Enqueue(() =>
{
try { _output = outputBuilder(_results); return Task.CompletedTask; }
catch (Exception ex) { return Task.FromException(ex); }
});
return this;
}
and you would consume it by calling each delegate one at a time and inspecting the task it returns:
public async Task<TOutput> Execute()
{
while (_pipe.TryDequeue(out var currentFunc))
{
var currentTask = currentFunc();
if (currentTask.IsCommandExecution())
{
IExecutionResult result = await (Task<IExecutionResult>)currentTask;
_results.Add(result);
}
else
{
await currentTask;
}
}
return _output;
}
Okay, thank you. I've ended up with such class and Queue<Func<Task>> like you said.
public sealed class SyncTaskWrapper
{
private Func<Task> _action;
public SyncTaskWrapper(Action action)
=> _action = CreateFunc(action);
private static Func<Task> CreateFunc(Action action)
=> () =>
{
try
{
action();
return Task.CompletedTask;
}
catch (Exception exception)
{
return Task.FromException(exception);
}
};
public static implicit operator Func<Task>(SyncTaskWrapper #this)
=> #this._action;
}
with usage
_pipe.Enqueue(new SyncTaskWrapper(() =>
_output = outputBuilder(_results)));

Async func is not awaiting

I am writing a wrapper function that executes an arbitrary number of async tasks and will provide retry and error handling policy. I'm having an issue awaiting the result of the async tasks.
The method call looks like this:
Execute(async () => await someAsyncFunction(someValue), async () await someOtherFunction(someValue))
My method implementation looks like this:
void Execute<T1, T2>(Func<T1> fn1, Func<T2> fn2, ... /* overloads for up to 6 functions */)
{
fn1();
fn2();
/* ... */
}
I've not yet applied the error handling and retry policy, but from debugging I've noticed that stepping over fn1 or fn2 is immediate, even when I put a large delay in, for example:
async Task someAsyncFunction(object value)
{
await Task.Delay(10000);
//...
}
Is it possible to achieve what I want with async methods?
An async "action" is actually a function that returns a Task, so it'll be a Func<Task>. You can create a collection of tasks and then await them all with Task.WhenAll. You can supply a flexible number of arguments using the params keyword.
Note also that Execute() must itself be async in order to make async calls.
public class Program
{
static async Task Execute(params Func<Task>[] actions)
{
var tasks = actions.Select( action => action() );
await Task.WhenAll(tasks);
}
public static async Task MainAsync()
{
await Execute
(
async () =>
{
await Task.Delay(3000);
Console.WriteLine("Function 1 done");
}
,
async () =>
{
await Task.Delay(3000);
Console.WriteLine("Function 2 done");
}
);
}
public static void Main()
{
MainAsync().GetAwaiter().GetResult();
}
}
Output:
Function 2 done
Function 1 done
Example code on DotNetFiddle

Why does this code ignore the await and proceed anyway?

I have the following code:
public Index () {
InitializeIndexAsync();
}
async Task InitializeIndexAsync () {
State = IndexState.Initializing;
await Task.Factory.StartNew(async () => {
// Initialize other things.
await IndexAsync();
});
State = IndexState.Ready;
}
I would expect that "State = IndexState.Ready" would not be hit until the asynchronous lambda completes, but debugging shows that line is hit long before the thread started above it completes. Why is this?
StartNew does not understand async lambdas, so when you pass it an async lambda, it will return a Task<Task>. Conceptually, the "outer" task only represents the start of the async lambda; the "inner" task represents the completion of the async lambda.
This is one of the reasons that StartNew is the wrong choice for async code, as I explain on my blog. A better solution is to use Task.Run, which was designed with async in mind:
async Task InitializeIndexAsync () {
State = IndexState.Initializing;
await Task.Run(async () => {
// Initialize other things.
await IndexAsync();
});
State = IndexState.Ready;
}
Not sure what you are trying to achieve by all these awaits...
I would try to keep it simple by having a synchronously method which initializes things, and then another MethodAsync which returns a Task and I can await on that task:
public async void Index()
{
await InitializeIndexAsync();
}
private Task InitializeIndexAsync()
{
return Task.Factory.StartNew(() => InitializeIndex());
}
private void InitializeIndex()
{
State = IndexState.Initializing;
// Initialize other things synchronously.
IndexAsync().Wait();
State = IndexState.Ready;
}
I hope this is what you meant.

Categories