Unit test Timeout in C# - c#

I have a Function doSomething() which should work in a specific Time (15 seconds). If Time is out then i should throw a message that says "Timeout" but i will still waiting for the result.
so i solve that with a task:
Task<A> mytask = Task.Factory.StartNew(() => ClassA.doSomething()) ;
Mytask.wait(15);
If(mytask.isCompleted)
{
result= mytask.Result ;
}
else
{
Debug(‘ Time is out ’) ;
Mytask.wait() ;
result= mytask.Result ;
}
my Question is : how i can unit test the senario of getting Timeout
my Try :
ClassA objectA= new ClassA() ;
Clocktime t.start() ;
Expect.Call(objectA.doSomething()) ;
t.end
Assert.isTrue(T > 15) ;
Mocks.verifyAll() ;

Disclaimer: Even though the scenario in the question does not follow best practices, it is still useful to demonstrate how to use TaskCompletionSource for testing task-related code.
Below is an example of how you can test the second branch - the "timeout" branch in your code using TaskCompletionSource
Do:
Prefer async code over sync - not to block the threads (of course, it depends)
Don't block on async threads
Use parameters and named constants instead of "magic values"
Keep your unit tests fast and focused on a single scenario
Avoid testing built-in functionality, like Wait timeout
Implement cooperative cancellation https://learn.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation
Use proper instrumentation - logging long running task is just an example, in PROD use ETW - https://learn.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
I have modified your code a bit, to align to these recommendations.
I hope it's clear, if not - please let me know how I can help :-)
// This is the class that does the "long running work"
public class ClassA<T>
{
public async Task<T> doSomething(CancellationToken cancellationToken)
{
const int magicNumberOfIterations = 100;
var magicLengthOfTimeInSeconds = TimeSpan.FromSeconds(1);
for (int i = 0; i < magicNumberOfIterations; i++)
{
if (cancellationToken.IsCancellationRequested)
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(magicLengthOfTimeInSeconds); // time - consuming work
}
return default;
}
}
// This is the class that "detects" long running work based on timeout
public class SlowTaskLogger<T> {
// Don't use this in prod - this actually blocks a thread to write a metric
// Use ETW instead - https://learn.microsoft.com/en-us/windows/win32/etw/event-tracing-portal
public Task<T> RunAndLogSlowRunningTask(TimeSpan logAfterTimeout, Task<T> task)
{
if (task.Wait(logAfterTimeout)) // DO NOT use in PROD
return task; // don't block on async threads
LogWarningForLongRunningTask(task);
return task;
}
private void LogWarningForLongRunningTask(Task<T> task)
{
longRunningTasks.Add(task); // TODO: logging
}
private List<Task<T>> longRunningTasks = new(); // NOTE: for testing, replace with partial mock
public IReadOnlyCollection<Task<T>> LongRunningTasks => longRunningTasks;
}
The test could look something like this
public class InterviewTaskTests
{
private SlowTaskLogger<string> slowTaskLogger = new();
[Fact]
public async Task LogSlowRunningTaskTest()
{
var timeout = TimeSpan.FromMilliseconds(15);
var completionSource = new TaskCompletionSource<string>();
var expectedResult = "foo";
var taskWithLogging = slowTaskLogger.RunAndLogSlowRunningTask(timeout, completionSource.Task);
await Task.Delay(timeout);
// check that timeout method was called
// you can use partial mocks instead of private state for production code
Assert.Contains(taskWithLogging, slowTaskLogger.LongRunningTasks);
// complete the task and ensure proper result is returned
completionSource.SetResult(expectedResult);
var actualResult = await taskWithLogging;
Assert.Equal(expectedResult, actualResult);
}
}

Related

Using cancellation token properly in c#

I was recently exposed to C# language and was working on getting data out of cassandra so I was working with below code which gets data from Cassandra and it works fine.
Only problem I have is in my ProcessCassQuery method - I am passing CancellationToken.None to my requestExecuter Function which might not be the right thing to do. What should be the right way to handle that case and what should I do to handle it correctly?
/**
*
* Below method does multiple async calls on each table for their corresponding id's by limiting it down using Semaphore.
*
*/
private async Task<List<T>> ProcessCassQueries<T>(IList<int> ids, Func<CancellationToken, int, Task<T>> mapperFunc, string msg) where T : class
{
var tasks = ids.Select(async id =>
{
await semaphore.WaitAsync();
try
{
ProcessCassQuery(ct => mapperFunc(ct, id), msg);
}
finally
{
semaphore.Release();
}
});
return (await Task.WhenAll(tasks)).Where(e => e != null).ToList();
}
// this might not be good idea to do it. how can I improve below method?
private Task<T> ProcessCassQuery<T>(Func<CancellationToken, Task<T>> requestExecuter, string msg) where T : class
{
return requestExecuter(CancellationToken.None);
}
As said in the official documentation, the cancellation token allows propagating a cancellation signal. This can be useful for example, to cancel long-running operations that for some reason do not make sense anymore or that are simply taking too long.
The CancelationTokenSource will allow you to get a custom token that you can pass to the requestExecutor. It will also provide the means for cancelling a running Task.
private CancellationTokenSource cts = new CancellationTokenSource();
// ...
private Task<T> ProcessCassQuery<T>(Func<CancellationToken, Task<T>> requestExecuter, string msg) where T : class
{
return requestExecuter(cts.Token);
}
Example
Let's take a look at a different minimal/dummy example so we can look at the inside of it.
Consider the following method, GetSomethingAsync that will yield return an incrementing integer every second.
The call to token.ThrowIfCancellationRequested will make sure a TaskCanceledException is thrown if this process is cancelled by an outside action. Other approaches can be taken, for example, check if token.IsCancellationRequested is true and do something about it.
private static async IAsyncEnumerable<int> GetSomethingAsync(CancellationToken token)
{
Console.WriteLine("starting to get something");
token.ThrowIfCancellationRequested();
for (var i = 0; i < 100; i++)
{
await Task.Delay(1000, token);
yield return i;
}
Console.WriteLine("finished getting something");
}
Now let's build the main method to call the above method.
public static async Task Main()
{
var cts = new CancellationTokenSource();
// cancel it after 3 seconds, just for demo purposes
cts.CancelAfter(3000);
// or: Task.Delay(3000).ContinueWith(_ => { cts.Cancel(); });
await foreach (var i in GetSomethingAsync(cts.Token))
{
Console.WriteLine(i);
}
}
If we run this, we will get an output that should look like:
starting to get something
0
1
Unhandled exception. System.Threading.Tasks.TaskCanceledException: A task was canceled.
Of course, this is just a dummy example, the cancellation could be triggered by a user action, or some event that happens, it does not have to be a timer.

Identifying when long running async tasks have completed

[Updated 18-Apr-2018 with LinqPad example - see end]
My application receives a list of jobs:
var jobs = await myDB.GetWorkItems();
(NB: we use .ConfigureAwait(false) everwhere, I'm just not showing it in these pseudo code snippets.)
For each job, we create a long running Task. However, we don't want to wait for this long running Task to complete.
jobs.ForEach(job =>
{
var module = Factory.GetModule(job.Type);
var task = Task.Run(() => module.ExecuteAsync(job.Data));
this.NonAwaitedTasks.Add(task, module);
};
The task and its related module instance are both added to a ConcurrentDictionary so that they don't go out of scope.
Elsewhere, I have another method that is called occasionally which contains the following:
foreach (var entry in this.NonAwaitedTasks.Where(e => e.Key.IsCompleted))
{
var module = entry.Value as IDisposable;
module?.Dispose();
this.NonAwaitedTasks.Remove(entry.Key);
}
(NB the NonAwaitedTasks is additionally locked using a SemaphoreSlim...)
So, the idea is that this method will find all those Tasks which have completed and then dispose of their related module, and remove them from this Dictionary.
However....
Whilst debugging in Visual Studio 2017, I pull a single job from the DB and whilst I'm taking my time debugging within the single Module that has been instantiated, the Dispose is called on that module. Looking in the Callstack, I can see the Dispose has been called in the method above, and that is because the task has IsCompleted == true. But evidently, it can't be completed because I'm still debugging it.
Is the .IsCompleted property the wrong property to check?
Is this just an artifact of debugging in Visual Studio?
Am I going about this the wrong way?
Additional Information
In the comments below, I was asked to provide some additional information regarding the flow because what I described didn't seem possible (and indeed, my hope was that it couldn't be). Below is a cut-down version of my code (I've removed checks for the cancellation token and defensive coding, but nothing that affects the flow).
Application Entry Point
This is a Windows Service. In the OnStart() is the following line:
this.RunApplicationTask =
Task.Run(() => myApp.DoWorkAsync().ConfigureAwait(false), myService.CancelSource.Token);
"RunApplicationTask" is just a property to keep the executing task in scope during the lifetime of the Service.
DoWorkAsync()
public async Task DoWorkAsync()
{
do
{
await this.ExecuteSingleIterationAsync().ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
}
while (myApp.ServiceCancellationToken.IsCancellationRequested == false);
await Task.WhenAll(this.NonAwaitedTasks.Keys).ConfigureAwait(false);
await this.ClearCompletedTasksAsync().ConfigureAwait(false);
this.WorkItemsTaskCompletionSource.SetResult(true);
return;
}
So whilst I'm debugging, this is iterating the DO-LOOP, it does not get to the Task.WhenAll(....).
Note too that after the Cancellation request is called and all Tasks have completed, I call ClearCompletedTasksAsync(). More on that later....
ExecuteSingleIterationAsync
private async Task ExecuteSingleIterationAsync()
{
var getJobsResponse = await DB.GetJobsAsync().ConfigureAwait(false);
await this.ProcessWorkLoadAsync(getJobsResponse.Jobs).ConfigureAwait(false);
await this.ClearCompletedTasksAsync().ConfigureAwait(false);
}
ProcessWorkLoadAsync
private async Task ProcessWorkLoadAsync(IList<Job> jobs)
{
if (jobs.NoItems())
{
return ;
}
jobs.ForEach(job =>
{
// The processor instance is disposed of when removed from the NonAwaitedTasks collection.
IJobProcessor processor = ProcessorFactory.GetProcessor(workItem, myApp.ServiceCancellationToken);
try
{
var task = Task.Run(() => processor.ExecuteAsync(job).ConfigureAwait(false), myApp.ServiceCancellationToken);
this.NonAwaitedTasks.Add(task, processor);
}
catch (Exception e)
{
...
}
});
return;
}
Each processor implements the following interface method:
Task ExecuteAsync(Job job);
It's whilst I'm in the ExecuteAsync that .Dispose() gets called on the processor instance I'm using.
ProcessorFactory.GetProcessor()
public static IJobProcessor GetProcessor(Job job, CancellationToken token)
{
.....
switch (someParamCalculatedAbove)
{
case X:
{
return new XProcessor(...);
}
case Y:
{
return new YProcessor(...);
}
default:
{
return null;
}
}
}
So here we're getting a new instance.
ClearCompletedTasksAsync()
private async Task ClearCompletedTasksAsync()
{
await myStatic.NonAwaitedTasksPadlock.WaitAsync().ConfigureAwait(false);
try
{
foreach (var taskEntry in this.NonAwaitedTasks.Where(entry => entry.Key.IsCompleted).ToArray())
{
var processorInstance = taskEntry.Value as IDisposable;
processorInstance?.Dispose();
this.NonAwaitedTasks.Remove(taskEntry.Key);
}
}
finally
{
myStatic.NonAwaitedTasksPadlock.Release();
}
}
This is called every iteration of the Do-Loop. It's purpose is to ensure that the list of non-awaited tasks is kept small.
And that's it... Dispose only seems to get called when debugging.
LinqPad example
async Task Main()
{
SetProcessorRunning();
await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
do
{
foreach (var entry in NonAwaitedTasks.Where(e => e.Key.IsCompleted).ToArray())
{
"Task is completed, so will dispose of the Task's processor...".Dump();
var p = entry.Value as IDisposable;
p?.Dispose();
NonAwaitedTasks.Remove(entry.Key);
}
}
while (NonAwaitedTasks.Count > 0);
}
// Define other methods and classes here
public void SetProcessorRunning()
{
var p = new Processor();
var task = Task.Run(() => p.DoWorkAsync().ConfigureAwait(false));
NonAwaitedTasks.Add(task, p);
}
public interface IProcessor
{
Task DoWorkAsync();
}
public static Dictionary<Task, IProcessor> NonAwaitedTasks = new Dictionary<Task, IProcessor>();
public class Processor : IProcessor, IDisposable
{
bool isDisposed = false;
public void Dispose()
{
this.isDisposed = true;
"I have been disposed of".Dump();
}
public async Task DoWorkAsync()
{
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
if (this.isDisposed)
{
$"I have been disposed of (isDispose = {this.isDisposed}) but I've not finished work yet...".Dump();
}
await Task.Delay(TimeSpan.FromSeconds(5)).ConfigureAwait(false);
}
}
Output:
Task is completed, so will dispose of the Task's processor...
I have been disposed of
I have been disposed of (isDispose = True) but I've not finished work
yet...
Your problem is in this line:
var task = Task.Run(() => p.DoWorkAsync().ConfigureAwait(false));
Hover over the var and take a look at what type that is.
Task.Run understands async delegates by having special "task unwrapping" rules for Func<Task<Task>> and friends. But it won't have any special unwrapping for Func<ConfiguredTaskAwaitable>.
You can think of it this way; with the code above:
p.DoWorkAsync() returns a Task.
Task.ConfigureAwait(false) returns a ConfiguredTaskAwaitable.
So the Task.Run is being asked to run this function that creates a ConfiguredTaskAwaitable on a thread pool thread.
Thus, the return type of Task.Run is Task<ConfiguredTaskAwaitable> - a task that completes as soon as the ConfiguredTaskAwaitable is created. When it is created - not when it completes.
In this case, the ConfigureAwait(false) isn't doing anything anyway, because there's no await to configure. So you can remove it:
var task = Task.Run(() => p.DoWorkAsync());
Also, as Servy mentioned, if you don't need to run DoWorkAsync on a thread pool thread, you can also skip the Task.Run:
var task = p.DoWorkAsync();

Why does this async unit test block the thread forever?

Objective
I want to compose asynchronous workflows in C# using Task<T> with custom extension methods. I want to unit test each of the basic extension methods I will use to ensure that they work properly, so there are no surprises in behavior when I start composing them together. One of the most important aspects of this testing is making sure that these methods run tasks when they should, NOT immediately when the workflow is being composed, but only when the composed workflow is awaited.
In practice, my extension methods seem to be working fine. However, I have not been able to create a unit test that tests the awaiting aspect of these methods without blocking the test thread.
I am using Visual Studio 2015, C# 5, .NET 4.5.1, and NUnit 3.
Code
Here is one such extension method I would like to test:
public static async Task<T> Let<T>(this Task<T> source, Action<T> action)
{
var result = await source;
action(result);
return result;
}
Here are the tests I've made for that method. (This project is using Shouldly which provides a fluent interface for NUnit, so you can write x.ShouldBe(3) to mean Assert.AreEqual(3, x).)
[TestFixture, Category("Task")]
public class WhenLettingTask {
private static bool sourceExecuted;
private static int sideEffectResult;
private static Task<int> Source =>
new Task<int>(() => {
sourceExecuted = true;
return 1;
});
private static readonly Action<int> SideEffect =
n => { sideEffectResult = n; };
[SetUp]
public void TestSetup() {
sourceExecuted = false;
sideEffectResult = 0;
}
[Test]
public void ShouldNotExecuteAnythingImmediately() {
var composed = Source.Let(SideEffect);
sourceExecuted.ShouldBeFalse();
sideEffectResult.ShouldBe(0);
}
[Test]
public async Task ReturnedTaskShouldExecuteInputsOnlyOnce() {
var composed = Source.Let(SideEffect);
var result = await composed; //Blocks forever
sourceExecuted.ShouldBeTrue();
sideEffectResult.ShouldBe(1);
sourceExecuted = false;
sideEffectResult = 0;
for (var i = 0; i < 10; i++) {
result = await composed;
sourceExecuted.ShouldBeFalse();
sideEffectResult.ShouldBe(0);
}
}
}
The first test works as expected, but the second one blocks on the first await forever.
Research
Interestingly, if I remove the await inside the method under test, the test will not block.
public static async Task<T> Let<T>(this Task<T> source, Action<T> action)
{
T result = default(T);
action(result);
return result;
}
In looking for help regarding async testing, I've seen a lot of posts recommending the use of Task.FromResult to get async tests to work, but this is basically short-circuiting the awaiting aspect, since a Task<T> created this way starts with a status of RanToCompletion and never needs to be awaited.
Resolution
Based on the answer from Scott Chamberlain, I changed the Source property in my test fixture to this:
private static Task<int> Source =>
Task.Run<int>(() => {
Thread.Sleep(1000);
sourceExecuted = true;
return 1;
});
Task.Run will start the task immediately, which allows my 2nd test to pass. Thread.Sleep is required so that the 1st test still passes.
You never start the task, you have what is known as a "cold task". Await does not start tasks, it only waits for them to finish.
You should never need to call new Task ( unless you are writing a task scheduler, use Task.Run ( instead to create a hot task that is already in the running state once the function returns.

Verify that task is being awaited

I have the following code which i'd like to test:
private Task _keepAliveTask; // get's assigned by object initializer
public async Task EndSession()
{
_cancellationTokenSource.Cancel(); // cancels the _keepAliveTask
await _logOutCommand.LogOutIfPossible();
await _keepAliveTask;
}
It is important that the EndSession Task only ends once the `_keepAliveTask' ended. However, I'm struggling to find a way to test it reliably.
Question: How do i unit test the EndSession method and verify that the Task returned by EndSession awaits the _keepAliveTask.
For demonstration purposes, the unit test could look like that:
public async Task EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAliveTask = new Mock<Task>();
// for simplicity sake i slightly differ from the other examples
// by passing the task as method parameter
await EndSession(keepAliveTask);
keepAliveTask.VerifyAwaited(); // this is what i want to achieve
}
Further criterias:
- reliable test (always passes when implementation is correct, always fails when implementation is wrong)
- cannot take longer than a few milliseconds (it's a unit test, after all).
I have already taken several alternatives into considerations which i'm documenting below:
non-async method
If there wouldn't be the call to _logOutCommand.LogOutIfPossible() it would be quite simple: i'd just remove the async and return _keepAliveTask instead of awaiting it:
public Task EndSession()
{
_cancellationTokenSource.Cancel();
return _keepAliveTask;
}
The unit test would look (simplified):
public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAliveTask = new Mock<Task>();
// for simplicity sake i slightly differ from the other examples
// by passing the task as method parameter
Task returnedTask = EndSession(keepAliveTask);
returnedTask.Should().be(keepAliveTask);
}
However, there's two arguments against this:
i have multiple task which need awaiting (i'm considering Task.WhenAll further down)
doing so only moves the responsibility to await the task to the caller of EndSession. Still will have to test it there.
non-async method, sync over async
Of course, I could do something similar:
public Task EndSession()
{
_cancellationTokenSource.Cancel(); // cancels the _keepAliveTask
_logOutCommand.LogOutIfPossible().Wait();
return _keepAliveTask;
}
But that is a no-go (sync over async). Plus it still has the problems of the previous approach.
non-async method using Task.WhenAll(...)
Is a (valid) performance improvement but introduces more complexity:
- difficult to get right without hiding a second exception (when both fail)
- allows parallel execution
Since performance isn't key here i'd like to avoid the extra complexity. Also, previously mentioned issue that it just moves the (verification) problem to the caller of the EndSession method applies here, too.
observing effects instead of verifying calls
Now of course instead of "unit" testing method calls etc. I could always observe effects. Which is: As long as _keepAliveTask hasn't ended the EndSession Task mustn't end either. But since I can't wait indefinite one has to settle for a timeout. The tests should be fast so a timeout like 5 seconds is a no go. So what I've done is:
[Test]
public void EndSession_MustWaitForKeepAliveTaskToEnd()
{
var keepAlive = new TaskCompletionSource<bool>();
_cancelableLoopingTaskFactory
.Setup(x => x.Start(It.IsAny<ICancelableLoopStep>(), It.IsAny<CancellationToken>()))
.Returns(keepAlive.Task);
_testee.StartSendingKeepAlive();
_testee.EndSession()
.Wait(TimeSpan.FromMilliseconds(20))
.Should().BeFalse();
}
But I really really dislike this approach:
hard to understand
unreliable
or - when it's quite reliable - it takes a long time (which unit tests shouldn't).
If all you want is to verify that EndSession is awaiting _keepAliveTask (and you really have full control over _keepAliveTask) then you can create your own awaitable type instead of Task the signals when it's awaited and check that:
public class MyAwaitable
{
public bool IsAwaited;
public MyAwaiter GetAwaiter()
{
return new MyAwaiter(this);
}
}
public class MyAwaiter
{
private readonly MyAwaitable _awaitable;
public MyAwaiter(MyAwaitable awaitable)
{
_awaitable = awaitable;
}
public bool IsCompleted
{
get { return false; }
}
public void GetResult() {}
public void OnCompleted(Action continuation)
{
_awaitable.IsAwaited = true;
}
}
Since all you need to await something is that has a GetAwaiter method that returns something with IsCompleted, OnCompleted and GetResult you can use the dummy awaitable to make sure _keepAliveTask is being awaited:
_keepAliveTask = new MyAwaitable();
EndSession();
_keepAliveTask.IsAwaited.Should().BeTrue();
If you use some mocking framework you can instead make Task's GetAwaiter return our MyAwaiter.
Use TaskCompletionSource and set its result at a known time.
Verify that before setting the result, the await on EndSession hasn't completed.
Verify that after setting the result, the await on EndSession has completed.
A simplified version could look like the following (using nunit):
[Test]
public async Task VerifyTask()
{
var tcs = new TaskCompletionSource<bool>();
var keepAliveTask = tcs.Task;
// verify pre-condition
Assert.IsFalse(keepAliveTask.IsCompleted);
var waitTask = Task.Run(async () => await keepAliveTask);
tcs.SetResult(true);
await waitTask;
// verify keepAliveTask has finished, and as such has been awaited
Assert.IsTrue(keepAliveTask.IsCompleted);
Assert.IsTrue(waitTask.IsCompleted); // not needed, but to make a point
}
You can also add a short delay at the waitTask to ensure any synchronous execution would be faster, something like:
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
});
And if you don't trust your unit test framework to deal correctly with async, you can set a completed flag as part of the waitTask, and check for that in the end. Something like:
bool completed = false;
var waitTask = Task.Run(async () =>
{
await Task.Delay(1);
await keepAliveTask;
completed = true;
});
// { .... }
// at the end of the method
Assert.IsTrue(completed);

async / await - am I correctly running these methods in parallel?

I have an abstract class called VehicleInfoFetcher which returns information asynchronously from a WebClient via this method:
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID);
I'd like to combine the results of two separate instances of this class, running each in parallel before combining the results. This is done within a third class, CombinedVehicleInfoFetcher (also itself a subclass of VehicleInfoFetcher)
Here's my code - but I'm not quite convinced that it's running the tasks in parallel; am I doing it right? Could it be optimized?
public class CombinedVehicleInfoFetcher : VehicleInfoFetcher
{
public HashSet<VehicleInfoFetcher> VehicleInfoFetchers { get; set; }
public override async Task<DTOrealtimeinfo> getVehicleInfo(string stopID)
{
// Create a list of parallel tasks to run
var resultTasks = new List<Task<DTOrealtimeinfo>>();
foreach (VehicleInfoFetcher fetcher in VehicleInfoFetchers)
resultTasks.Add(fetcher.getVehicleInfo(stopID, stopID2, timePointLocal));
// run each task
foreach (var task in resultTasks)
await task;
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
var allRealtimeResults = new List<DTOrealtimeinfo>( resultTasks.Select(t => t.Result) );
return combineTaskResults(allRealtimeResults);
}
DTOrealtimeinfo combineTaskResults(List<DTOrealtimeinfo> realtimeResults)
{
// ...
return rtInfoOutput;
}
}
Edit
Some very helpful answers, here is a re-written example to aid discussion with usr below:
public override async Task<object> combineResults()
{
// Create a list of parallel tasks to run
var resultTasks= new List<object>();
foreach (AnotherClass cls in this.OtherClasses)
resultTasks.Add(cls.getResults() );
// Point A - have the cls.getResults() methods been called yet?
// Wait for all the results to come in
await Task.WhenAll(resultTasks.ToArray());
// combine the results
return new List<object>( resultTasks.Select(t => t.Result) );
}
}
Almost all tasks start out already started. Probably, whatever fetcher.getVehicleInfo returns is already started. So you can remove:
// run each task
foreach (var task in resultTasks)
await task;
Task.WhenAll is faster and has better error behavior (you want all exceptions to be propagated, not just the first you happen to stumble upon).
Also, await does not start a task. It waits for completion. You have to arrange for the tasks to be started separately, but as I said, almost all tasks are already started when you get them. This is best-practice as well.
To help our discussion in the comments:
Task Test1() { return new Task(() => {}); }
Task Test2() { return Task.Factory.StartNew(() => {}); }
Task Test3() { return new FileStream("").ReadAsync(...); }
Task Test4() { return new TaskCompletionSource<object>().Task; }
Does not "run" when returned from the method. Must be started. Bad practice.
Runs when returned. Does not matter what you do with it, it is already running. Not necessary to add it to a list or store it somewhere.
Already runs like (2).
The notion of running does not make sense here. This task will never complete although it cannot be explicitly started.

Categories