How a task is constructed when using Invoke on a delegate? - c#

delegate Task MyDelegate();
event MyDelegate d;
d += () => Task.Delay(100);
d += () => Task.Delay(200);
d += () => Task.Delay(300);
var t = d.Invoke();
await Task.Delay(1000);
After invoking by Invoke status is:
Id = 1, Status = WaitingForActivation, Method = {null}.
After delay, so when all tasks are finished it's:
Id = 1, Status = RanToCompletion, Method = {null}.
Does it mean that Invoke executes Task.WhenAll implicitly or it's internal mechanism is even something different?

Delegate combining has no knowledge of Task or await. As you note, it will execute all of them and return the last one. The delegate will simply drop the others on the floor and only return the last.
But in this case, Task.Delay does not execute anything, it just returns a "promise" which you have to await. If you do not await it then no delay happens.
And that final result you are also dropping on the floor, as you do not await t. The only Delay actually working is the final await Task.Delay(1000);
delegate Task MyDelegate();
event MyDelegate d;
d += () => Task.Delay(100); // Just returns a Task when executed, does NOT block
d += () => Task.Delay(200);
d += () => Task.Delay(300);
// Nothing has happened yet!!
var t = d.Invoke();
// All delegates are executed synchronously, but only the final result is returned.
// Each delegate returned a Task but did not block for the delay amount.
// Result is also dropped so no delay happens at all.
await Task.Delay(1000);
// Final real delay of 1000
If you had changed the last line to await t; instead, you would have waited for 300ms, not the total 600ms.
You can see all this in action in this dotnetfiddle.

After quick experiment with integers seems it returns last delegate result:
delegate int MyDelegate();
event MyDelegate d;
d += () => 3;
d += () => 1;
d += () => 2;
var t = d.Invoke(); // t = 2
So in the original case with tasks, the last added delegate's task is returned.

Related

.Result operation doesn't block

I expect the following code to be blocked for almost 5 secs, but it is not. It immediately prints. Isn't Result operation blocking?
class Program
{
static void Main()
{
// Return a value type with a lambda expression
Task<int> task1 = Task<int>.Factory.StartNew(() => Task.Delay(5000).Id + 100);
int i = task1.Result;
Console.WriteLine(i);
}
}
This code is not waiting for Delay to finish. It starts the delay and then return immediately Id+100. So, when the code reaches the Result operation, the task1 is almost always is in Completed state, so you get Result immediately.
You can try following to get desired behaviour
Task<int> task1 = Task.Run(async () => await Task.Delay(5000).Id + 100);
int i = task1.Result;
Or better, use await instead of Result everywhere
Task<int> task1 = Task.Run(async () => await Task.Delay(5000).Id + 100);
int i = await task1;
or even
int i = await Task.Delay(5000).Id + 100
(but I'm unsure here as you may have more logic inside the task in actual code)
To me ContinueWith seems more natural for what you're trying to do:
Task<int> t = Task.Delay(5000).ContinueWith(delayTask => delayTask.Id + 100);
Console.WriteLine(t.Result);
This will execute the delay task and then execute the lambda in ContinueWith after the delay is complete.

await AsyncEventHandler<T> - only the last registered is awaited

I'm using the AsyncEventHandler to define events and try to await them.
The event definition is straigt forward:
public event AsyncEventHandler<EventArgs> Sample;
The problem occurs on awaiting the registered handlers. I'm using code like this to register and await the invocation:
var t0Finished = false;
this.Sample += (_, __) => Task.Run(() => t0Finished = true);
await (this.Sample?.Invoke(this, EventArgs.Empty) ?? Task.CompletedTask);
// t0Finished == true
To me it looks okay and works as expected until a second event handler is registered:
var t0Finished = false;
var t1Finished = false;
this.Sample += (_, __) => Task.Run(async () => { await Task.Delay(1000); t0Finished = true; });
this.Sample += (_, __) => Task.Run(() => t1Finished = true);
await (this.Sample?.Invoke(this, EventArgs.Empty) ?? Task.CompletedTask);
// t0Finished == false !!!!!
// t1Finished = true
The problem here is that only the last registered event handler is awaited.
DEMO
What am I missing here?
What is the intension of AsyncEventHandler<T> (the expected usage of it)?
My expectation was that all event handlers are running sequentially (to be compatible to the existing normal events that stop calling a second event handler if the first one throws an exception) and each of them is awaited. I could also live with an parallel invokation of all registered handlers but I would expect to behave like Task.WhenAll in that case. But it doesn't...
In my opinion the name AsyncEventHandler is confusing. It suggests that it is usable like normal events but gives you the ability to await async operations on that.
Currently I'm seeing only two ways to handle that:
Change the AsyncEventHandler<T> to a normal Func<T, Task> and change the event to a delegate property. That ensures that only one instance is assigned that could be awaited correctly.
Change all the events to standard EventHandler<T> and use async void in implementation because we can't await it anyway.
In both cases AsyncEventHandler<T> is not used any longer.
There is nothing special about AsyncEventHandler<EventArgs>; it is just a delegate type, that behaves exactly the same as any other than matches its signature:
public delegate Task AnyOldDelegate<TEventArgs>(object sender, TEventArgs args);
Whilst delegates and events are both multicast, the primary semantic difference between the two is that a delegate usually requires a single handler, whereas events can have zero or more subscribers; delegates are key to normal program flow, whereas events are peripheral.
For this reason it is generally a bad idea to try to return a value from an event, because there can only ever be a single value, irrespective of the number of subscribers, based on "last subscriber wins".
If you need multiple return values, this is a sign that you are using events incorrectly.
You could workaround using delegates:
Func<Task> Sample;
var t0Finished = false;
var t1Finished = false;
Sample += () => Task.Run(async () => { await Task.Delay(1000); t0Finished = true; });
Sample += () => Task.Run(() => t1Finished = true);
await Task.WhenAll(Sample?.GetInvocationList().Select(func => func()) ?? Enumerable.Empty<Task>());
Or be explicit, and provide a collection of delegates:
List<Func<Task>> Sample = new List<Func<Task>>();
var t0Finished = false;
var t1Finished = false;
Sample.Add(() => Task.Run(async () => { await Task.Delay(1000); t0Finished = true; }));
Sample.Add(() => Task.Run(() => t1Finished = true));
await Task.WhenAll(Sample.Select(func => func()));
And if you need sequential invocation:
foreach (Func<Task> func in Sample)
{
await func();
}
If you're wondering what the purpose of AsyncEventHandler<EventArgs> is, it has an extension method: InvokeAsync, which invokes all handlers, and returns a Task that completes when all handlers have been invoked.
This may or may not be a viable alternative depending on your situation.

Can the Elapsed callback of a System.Timers.Timer be async?

Is it possible (or even reasonable) to make the callback of a System.Timers.Timer an async method? Something like:
var timer = new System.Timers.Timer
{
Interval = TimeSpan.FromSeconds(30).TotalMilliseconds,
AutoReset = true
};
timer.Elapsed += async (sender, e) => { /* await something */ };
timer.Start();
It compiles (obviously a good place to start), but I'm not sure I understand the consequences. Will the timer await the callback before resetting the timer?
Will the timer await the callback before resetting the timer?
No. There's nothing it could await, because the signature of ElapsedEventHandler has a void return type.
In other words, your code is equivalent to:
var timer = new System.Timers.Timer { ... };
timer.Elapsed += Foo;
timer.Start();
...
private async void Foo()
{
...
}
Whether that's acceptable for you or not will depend on your context. In general, having async void methods or anonymous functions makes them harder to test and reuse - but the ability was precisely given for the sake of event handlers... You should consider how errors will be propagated though.
The title of the question is specifically about Timers, but if we look at it as "How to call an async method after some time?" then you could do it without using a timer.
var task2 = Task.Run(async () => {
while (true)
{
try
{
await MyMethod2();
} catch
{
//super easy error handling
}
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
...
public async Task MyMethod2()
{
//async work here
}
Please note however that this will have different timing (timer will be called at an interval, the code above will be called every (run time + sleep_time), but even if MyMethod2 takes a long time it it won't be called twice. Having said that, you can calculate how long to await for to run 'every x minutes'.
Actually, you can.
System.Timers.Timer timer = new System.Timers.Timer();
timer.Elapsed += async (x, y) => { await Task.Delay(1); };
The solution that proposed #tymtam doesn´t wait until MyMethod2 has ended.
I think it would be better to use this. An example with two async tasks, when both has finised, wait 5 seconds and execute again the two tasks:
var task2 = Task.Run(async () => {
while (true)
{
try
{
var task1 = MyMethod1();
var task2 = MyMethod2();
List<Task> allTasks = new List<Task> { task1, task2 };
while (allTasks.Count > 0)
{
Task finishedTask = await Task.WhenAny(allTasks);
if (finishedTask == task1)
{
Console.WriteLine("MyMethod1 has ended");
}
else if (finishedTask == task2)
{
Console.WriteLine("MyMethod2 has ended");
}
tareas.Remove(finishedTask);
}
//Here only when finished all task
} catch
{
//super easy error handling
}
//Wait until next cycle
await Task.Delay(TimeSpan.FromSeconds(5));
}
});
...
public async Task MyMethod1()
{
//async work here
}
public async Task MyMethod2()
{
//async work here
}

ReactiveExtensions Observable FromAsync calling twice Function

Ok, Trying to understand Rx, kinda of lost here.
FromAsyncPattern is now deprecated so I took the example from here (section Light up Task with Rx), and it works, I just made a few changes, not using await just wait the observable and subscribing.....
What I don't understand is Why is called Twice the function SumSquareRoots?
var res = Observable.FromAsync(ct => SumSquareRoots(x, ct))
.Timeout(TimeSpan.FromSeconds(5));
res.Subscribe(y => Console.WriteLine(y));
res.Wait();
class Program
{
static void Main(string[] args)
{
Samples();
}
static void Samples()
{
var x = 100000000;
try
{
var res = Observable.FromAsync(ct => SumSquareRoots(x, ct))
.Timeout(TimeSpan.FromSeconds(5));
res.Subscribe(y => Console.WriteLine(y));
res.Wait();
}
catch (TimeoutException)
{
Console.WriteLine("Timed out :-(");
}
}
static Task<double> SumSquareRoots(long count, CancellationToken ct)
{
return Task.Run(() =>
{
var res = 0.0;
Console.WriteLine("Why I'm called twice");
for (long i = 0; i < count; i++)
{
res += Math.Sqrt(i);
if (i % 10000 == 0 && ct.IsCancellationRequested)
{
Console.WriteLine("Noticed cancellation!");
ct.ThrowIfCancellationRequested();
}
}
return res;
});
}
}
The reason that this is calling SumSquareRoots twice is because you're Subscribing twice:
// Subscribes to res
res.Subscribe(y => Console.WriteLine(y));
// Also Subscribes to res, since it *must* produce a result, even
// if that result is then discarded (i.e. Wait doesn't return IObservable)
res.Wait();
Subscribe is the foreach of Rx - just like if you foreach an IEnumerable twice, you could end up doing 2x the work, multiple Subscribes means multiple the work. To undo this, you could use a blocking call that doesn't discard the result:
Console.WriteLine(res.First());
Or, you could use Publish to "freeze" the result and play it back to > 1 subscriber (kind of like how you'd use ToArray in LINQ):
res = res.Publish();
res.Connect();
// Both subscriptions get the same result, SumSquareRoots is only called once
res.Subscribe(Console.WriteLine);
res.Wait();
The general rule you can follow is, that any Rx method that doesn't return IObservable<T> or Task<T> will result in a Subscription(*)
* - Not technically correct. But your brain will feel better if you think of it this way.

.Continue starts before task is completed

I have the following code in C#, VS2012, WPF 4.5.
My expectation would be that, .ContinueWith will be executed after the task has finished completely (that's a continuation's whole purpose, isn't it?).
This should result in a value of 2 in finalResult.
int myTestInt = 0;
Task task = Task.Factory.StartNew(async () =>
{
myTestInt = 1;
await Task.Delay(TimeSpan.FromSeconds(6));
myTestInt = 2;
}).ContinueWith(_ =>
{
int finalResult = myTestInt;
});
In fact, finalResult is assigned a value of 1 instead. So it seems like the continuation is started on the await statement already.
Is this the intended behaviour? Am I missing something here? Can't I rely on ContinueWithto start after a task has completely finished?
Update:
Justin's answer just inspired me to check the following:
int myTestInt = 0;
Task task=Task.Factory.StartNew(async () =>
{
myTestInt = 1;
await Task.Delay(TimeSpan.FromSeconds(6));
myTestInt = 2;
});
task.Wait();
int result2 = myTestInt;
finalResult is still set to 1. Is there no way to reliably wait for a task that contains awaits to complete?
When you pass an async delegate to Task.Factory.StartNew, the returned Task only represents the first portion of that delegate (up until the time it awaits something that is not already completed).
However, if you pass an async delegate to the new Task.Run method (which was included for this reason), the returned Task represents the entire delegate. So you can use ContinueWith as you expect. (Although await is usually a better option than ContinueWith).
For more information on StartNew vs Run, see Stephen Toub's post on the topic.
The await will immediately return control to the calling function, which in this case is the StartNew of your task. This means the task will then complete and execute the ContinueWith.
If you really want task to complete before the ContinueWith, then don't await the Task.Delay.
I saw this in the MSDN: :-)
public async void button1_Click(object sender, EventArgs e)
{
pictureBox1.Image = await Task.Run(async() =>
{
using(Bitmap bmp1 = await DownloadFirstImageAsync())
using(Bitmap bmp2 = await DownloadSecondImageAsync())
return Mashup(bmp1, bmp2);
});
}
So do not forget the "async()" !!!

Categories

Resources