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.
Related
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.");
}
}
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, ...);
In ASP.NET WebAPi 2 code, I have a delegate handler
public class RequestHandler1 : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var formData2 = await ReadContent(request);
return await base.SendAsync(request, cancellationToken);
}
private static async Task<string> ReadContent(HttpRequestMessage request)
{
using (var ms = new MemoryStream())
{
await request.Content.CopyToAsync(ms);
ms.Seek(0, SeekOrigin.Begin);
using (var sr = new StreamReader(ms, Encoding.UTF8, true, 100, true))
{
return sr.ReadToEnd();
}
}
}
private static async Task<string> ReadContent3(HttpRequestMessage request)
{
var text = await request.Content.ReadAsStringAsync();
return text;
}
}
The issue is related to HttpContent.ReadAsStringAsync causes request to hang (or other strange behaviours) but it was never properly answered in that thread.
By the time I call return await base.SendAsync(request, cancellationToken); it just hangs. it does not matter if I call ReadContent or ReadContent3
Any more suggestions?
try this code
public class CustomLogHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var logMetadata = await BuildRequestMetadata(request);
var response = await base.SendAsync(request, cancellationToken);
logMetadata = await BuildResponseMetadata(logMetadata, response);
await SendToLog(logMetadata);
return response;
}
private async Task<LogMetadata> BuildRequestMetadata(HttpRequestMessage request)
{
LogMetadata log = new LogMetadata
{
RequestMethod = request.Method.Method,
RequestTimestamp = DateTime.Now,
RequestUri = request.RequestUri.ToString(),
RequestContent = await request.Content.ReadAsStringAsync(),
};
return log;
}
private async Task<LogMetadata> BuildResponseMetadata(LogMetadata logMetadata, HttpResponseMessage response)
{
logMetadata.ResponseStatusCode = response.StatusCode;
logMetadata.ResponseTimestamp = DateTime.Now;
logMetadata.ResponseContentType = response.Content == null ? string.Empty : response.Content.Headers.ContentType.MediaType;
logMetadata.Response = await response.Content.ReadAsStringAsync();
return logMetadata;
}
private async Task<bool> SendToLog(LogMetadata logMetadata)
{
try
{
//write your code
}
catch
{
return false;
}
return true;
}
}
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();
I have an issue with a task blocking when I try to retrieve it's result.
I have the following piece of code I want executed synchronously (which is why I'm looking for the result)
I would ignore the reason each call has to be made (legacy software that requires multiple calls through different layers)
the call seems to break down after it starts the task for the final call to be made in the PostCreateProfile, I can see this request never makes it any further than this.
if (CreateProfile(demographics).Result) // Task blocks here
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}
The request will reach its destination if I was to change CreateProfile to an async void like so:
private async void CreateProfile(AppointmentController controller)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(controller);
await profileService.Create(createProfileBindingModel);
}
But I can't return the bool I want to use from this.
Can anyone point out what I am doing wrong?
You should never call .Result on a async/await chain.
Whatever code that calls CreateProfile(demographics) needs to be async too so it can do
if (await CreateProfile(demographics))
{
//dothing
}
Also, if you can you really should put .ConfigureAwait(false) wherever it is logically possible.
if (await CreateProfile(demographics).ConfigureAwait(false)) // depending on what dothing is you may not want it here.
{
//dothing
}
private async Task<bool> CreateProfile(Demographics demographics)
{
ProfileService profileService = new ProfileService();
CreateProfileBindingModel createProfileBindingModel = this.CreateProfileModel(demographics);
return await profileService.Create(createProfileBindingModel).ConfigureAwait(false);
}
public async Task<bool> Create(CreateProfileBindingModel model)
{
HttpResponseMessage response = await profileServiceRequest.PostCreateProfile(rootURL, model).ConfigureAwait(false);
return response.IsSuccessStatusCode;
}
public Task<HttpResponseMessage> PostCreateProfile(string url, CreateProfileBindingModel model)
{
HttpContent contents = SerialiseModelData(model);
var resultTask = client.PostAsync(url, contents);
return resultTask;
}