How to get data from Task after running Task.WhenAll(taks) - c#

I have multiple Async methods that I can run one by one but When I Use Task.WhenAll, I really don't know how to get the result. Following are my Async methods
public async Task<IEnumerable<MyModel>> Method1Async()
{
return await Task<IEnumerable<MyModel>>.Run<IEnumerable<MyModel>>(() => GetMethod1Data());
}
//Here Method1Async().Result will get me the data but don't know how to get the result when I am using Task.WhenAll()
public async Task<IEnumerable<MyModel>> Method2Async()
{
return await Task<IEnumerable<MyModel>>.Run<IEnumerable<MyModel>>(() => GetMethod2Data());
}
public async Task<IEnumerable<MyModel>> Method3Async()
{
return await Task<IEnumerable<MyModel>>.Run<IEnumerable<MyModel>>(() => GetMethod3Data());
}
And here is my code where I am using Task.WhenAll
public async Task GetDataAsync()
{
Task[] tasks = new Task[3];
tasks[0] = Method1Async();
tasks[1] = Method2Async();
tasks[2] = Method2Async();
await Task.WhenAll(tasks).ConfigureAwait(false);
}
Now if I call
GetDataAsync()
method, I get all ok but don't know how to get the result from the returned task?

It will return an Array of type IEnumerable<MyModel> so you can set the return type of your async method to that :
public Task<IEnumerable<MyModel>[]> GetDataAsync()
{
Task[] tasks = new Task[3];
tasks[0] = staticDataService.Method1Async();
tasks[1] = staticDataService.Method2Async();
tasks[2] = staticDataService.Method3Async();
return Task.WhenAll(tasks);
}
and then consume it:
IEnumerable<MyModel>[] results = await GetDataAsync();

Related

Why Task.WhenAll return null?

I have two methods which return independent data. I assume it's a good idea to run them in parallel.
Without any modifications code look's like:
private async Task<Entity> MethodAsync()
{
...
var model1 = await client.Method1(Id1, cancellationToken);
var model2 = await client.Method2(Id2, cancellationToken);
...
}
Those methods return data as I expecting. Now when I change code all methods return "null". When I inspect Task object in visual studio there are properties Id = 2356, Status = RanToCompletion, Method = "{null}", Result = "".
private async Task<Entity> MethodAsync()
{
var model1Task = client.Method1(Id1, cancellationToken);
var model2Task = client.Method2(Id2, cancellationToken);
var task = Task.WhenAll(new Task[] { model1Task ,model2Task });
await task; //doesn't work
//task.Wait(); //doesn't work
//await Task.WhenAll(new Task[] { model1Task , model2Task }); //doesn't work
//Task.WhenAll(new Task[] { model1Task, model2Task}); //doesn't work
}
Code of client methods almost the same:
public async Task<Model> Method1(Guid Id, CancellationToken cancellationToken)
{
HttpResponseMessage responseMessage = await client.GetAsync($"customEndPoint");
ResponseMessageSingle<Model> response = JsonSerializer.Deserialize<ResponseMessageSingle<Model>>(
await responseMessage.Content.ReadAsStringAsync(cancellationToken),
new JsonSerializerOptions(JsonSerializerDefaults.Web));
return response.result;
}
private class ResponseMessageSingle<T>
{
public bool success { get; set; }
public string message { get; set; }
public T result { get; set; }
}
Also there is AuthorizeInterceptor(DelegatingHandler):
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request, CancellationToken cancellationToken)
{
some logic...
request.SetBearerToken(TokenResponse.AccessToken);
HttpResponseMessage response = await base.SendAsync(request, cancellationToken);
return await Task.FromResult(response);
}
Task.WhenAll does not return a result in your case, because you create an array of Task, which has no result. And since your tasks return different results, you cannot get that in an array nicely.
You should change your code like this to get the results of your methods:
private async Task<Entity> MethodAsync()
{
var model1Task = client.Method1(Id1, cancellationToken);
var model2Task = client.Method2(Id2, cancellationToken);
var task = Task.WhenAll(new Task[] { model1Task ,model2Task });
await task;
var result1 = await model1Task;
var result2 = await model2Task;
...
}
Since both methods will be completed, awaiting them will just immediately get you the results.

Set maximum runtime of a method

If I have a method where I want to perform some (potentially) long-running function and I want to put a limit on its execution time, I've been using this pattern (please pardon any errors in the code, typed by hand, not in an IDE, this is a simplification of a larger piece of code).
public string GetHello()
{
var task = Task.Run(() =>
{
// do something long running
return "Hello";
});
bool success = task.Wait(TimeSpan.FromMilliseconds(1000));
if (success)
{
return task.Result;
}
else
{
throw new TimeoutException("Timed out.");
}
}
If I want to use the GetHello method in an async capacity, i.e. public async Task<string> GetHello(), how would I do this while hopefully preserving a similar pattern? I have the following, but I get compiler warnings about This async method lacks 'await' operators and will run synchronously as expected.
public async Task<string> GetHello()
{
var task = Task.Run(async () =>
{
// await something long running
return "Hello";
});
bool success = task.Wait(TimeSpan.FromMilliseconds(1000));
if (success)
{
return task.Result;
}
else
{
throw new TimeoutException("Timed out.");
}
}
I just don't know how to change this or where I would put await in order for this to work as expected.
You can combine using CancellationToken and awaitable Task.WhenAny to achieve desired behavior:
public async Task<string> GetHello()
{
var cts = new CancellationTokenSource();
var task = Task.Run(async () =>
{
// await something long running and pass/use cts.Token here too
return "Hello";
}, cts.Token);
var delay = Task.Delay(1000, cts.Token);
var finishedFirst = await Task.WhenAny(task, delay);
cts.Cancel();
if (finishedFirst == task)
{
return task.Result;
}
else
{
throw new TimeoutException("Timed out.");
}
}

Task.Run fails to process awaited methods

We are running into a situation where have a requirement to start and execute few launch and forget threads during a call. Though, our call fails to execute if the async methods have any awaited call.
Here is an example. Are we missing something?
public class SomeClass
{
public async Task Test()
{
//Calling synchronously this things works
await Save(1).ConfigureAwait(false);
await Save(2).ConfigureAwait(false);
await Save(3).ConfigureAwait(false);
//Starting three threads at the same time fails while trying to run var queryResult = await SomeClient.QueryAsync<T>(q).ConfigureAwait(false);
_ = Task.Run(async () => await Save(1));
_ = Task.Run(async () => await Save(2));
_ = Task.Run(async () => await Save(3));
}
public async Task<bool> Save(int ct)
{
var x = await Update(ct).ConfigureAwait(false);
return x;
}
public async Task<bool> Update(int ct)
{
await _someObject.CallingSomeAsyncMethod<dynamic>("Some Query").ConfigureAwait(false);
await _someObject.CallingSomeAsyncMethod<dynamic>("Some Query").ConfigureAwait(false);
await _someObject.CallingSomeAsyncMethod<dynamic>("Some Query").ConfigureAwait(false);
return true;
}
}
public class SomeObject
{
public async Task<T> CallingSomeAsyncMethod(string q)
{
await Task.Delay(1000).ConfigureAwait(false);
//OR Any async method which is awaited here just stops the execution
return queryResult;
}
}
If you want to run multiple tasks at the same time you should call the methods without the await and hold the task. Then you can do await Task.WhenAll(task1, task2, task3, ...);

c# Writing an async method that doesn't have an await in it

I want to have an async method in my call that would be called by my library consumers with an await. But the internal working of my method, while I need it to run on a separate thread, does not call any other await on it's own.
I just do the work the method is supposed to do and return the type. And the compiler warns me that this method will not run in an async way.
public async Task<MyResultObject> DoSomeWork()
{
MyResultObject result = new MyResultObject();
// Some work to be done here
return result;
}
On the other hand, if I write a method that starts a new task using TaskFactory like this:
public Task<MyResultObject> DoSomeWork()
{
return Task<MyResultObject>.Factory.StartNew(() =>
{
MyResultObject result = new MyResultObject();
// Some work to be done here
return result;
});
}
Then I cannot call it using the await keyword like this await DoSomeWork().
How do I write an async (must be async and not task with result or wait) without using some await call inside?
You can do this
public Task<MyResultObject> DoSomeWork()
{
MyResultObject result = new MyResultObject();
// Some work to be done here
return Task.FromResult(result);
}
which is exactly the same as
public async Task<MyResultObject> DoSomeWork()
{
MyResultObject result = new MyResultObject();
// Some work to be done here
return result;
}
Only this version gives a warning and has slightly more overhead.
But neither will run on another thread. The only benefit is that they have an awaitable interface.
To do it in parallel, your code is Ok but Task.Run is preferred over StartNew():
public Task<MyResultObject> DoSomeWork()
{
return Task.Run(() =>
{
MyResultObject result = new MyResultObject();
// Some work to be done here
return result;
});
}
And in all these cases you can definitely await DoSomeWork()

Asynchronous population of collection initializer

I would like to populate a collection using collection initializer that will call async methods:
public class Diagnostics
{
public async Task<IEnumerable<DiagnosticsInfo>> Get() => new List<DiagnosticsInfo>
{
await GetDiagnosticsInfo1(),
await GetDiagnosticsInfo2(),
await GetDiagnosticsInfo3()
};
}
The aim is for all the GetDiagnosticsInfoX() methods to execute in parallel.
I have a suspicion that the generated code will invoke GetDiagnosticsInfoX() methods synchronously - it will await the first call before invoking the second.
Is my gut feel correct?
If so I assume I would need to await all the tasks like this:
public class Diagnostics
{
public async Task<IEnumerable<DiagnosticsInfo>> Get()
{
var task1 = GetDiagnosticsInfo1();
var task2 = GetDiagnosticsInfo2();
var task3 = GetDiagnosticsInfo3();
await Task.WhenAll(task1, task2, task3);
return new List<DiagnosticsInfo> {task1.Result, task2.Result, task3.Result};
}
}
Is my gut feel correct?
Your gut feeling is right. All collection initializer does is invoke the Add method for the collection. This means your code is translated to:
public async Task<IEnumerable<DiagnosticsInfo>> Get()
{
var list = new List<DiagnosticsInfo>();
list.Add(await GetDiagnosticsInfo1());
list.Add(await GetDiagnosticsInfo2());
list.Add(await GetDiagnosticsInfo3());
return list;
}
Using Task.WhenAll will wait for all async methods to complete. You can then return the array you receive:
public async Task<IEnumerable<DiagnosticsInfo>> Get()
{
var task1 = GetDiagnosticsInfo1();
var task2 = GetDiagnosticsInfo2();
var task3 = GetDiagnosticsInfo3();
return await Task.WhenAll(task1, task2, task3);
}

Categories

Resources