.NET CORE Time asynced method - c#

I have several asynec methods.
One of them triggers a POST method which start a process. I then need to 'sample' the results of another GET method every 10 minutes, and check if the status has changed from "pending" to "success" .
I tryed usingSystem.Threading.Timer with no luck, complaining about my method being asynced .
Error CS0407 'Task Campaigns.repeat(object)' has the wrong return type Campaigns
This is my code:
public async Task waitForCampaignLoadAsync(string Uri)
{
...........
var container = JsonConvert.DeserializeObject<CampaignTempleteStatus>(json);
if(container.status == "pending")
{
var autoEvent = new AutoResetEvent(false);
//The next row triggers the error
var stateTimer = new Timer(repeat, autoEvent, 1000, (1000 * 60 * 10));
//How can I keep repeating this, until (bool isFinished = true)??
}
public async Task repeat(Object stateInfo)
{
if(...)
isFinished = true;
}
Another thing is , how do I pass extra info inside repeat function? I need to pass the Uri input for inner ussage ?

When an asynchronous method starts getting complicated it's a sure sign something is wrong. Most of the time async code looks almost the same as synchronous code with the addition of await.
A simple polling loop could be as simple as :
public async Task<string> waitForCampaignLoadAsync(string uri)
{
var client=new HttpClient();
for(int i=0;i<30;i++)
{
token.ThrowIfCancellationRequested();
var json = await client.GetStringAsync(uri);
var container = JsonConvert.DeserializeObject<CampaignTempleteStatus>(json);
if (container.status != "pending")
{
return container.status;
}
await Task.Delay(10000);
}
return "Timed out!";
}
Cancellation in managed threads explains how CancellationTokenSource and CancellationToken can be used to cancel threads, tasks and asynchronous functions. Many asynchronous methods already provide overloads that accept a CancellationToken parameter. The polling function could be modified to accept and check a canellation token :
public async Task<string> waitForCampaignLoadAsync(string uri,CancellationToken token=default)
{
var client=new HttpClient();
for(int i=0;i<30;i++)
{
var json = await client.GetStringAsync(uri);
var container = JsonConvert.DeserializeObject<CampaignTempleteStatus>(json);
if (container.status != "pending")
{
return container.status;
}
await Task.Delay(10000,token);
}
return "Timed out!";
}
A CancellationTokenSource can be used to call this method with an overall timeout of eg, 5 minutes :
var cts=new CancellationTokenSource(TimeSpan.FromMinutes(5));
try
{
var result=waitForCampaignLoadAsync(uri,cts.Token);
//Process the result ....
}
catch(OperationCancelledExcepction ex)
{
//Handle the timeout here
}
This code can be improved. For example, GetStringAsync() doesn't accept a cancellation token. The operation can be broken in two steps though, one call to GetAsync() with a cancellation token that waits for the server to send a result
and another to HttpContent.ReadAsStringAsync() to read the response, eg :
var response=await client.GetAsync(uri,token)
response.EnsureSuccessStatusCode();
var json=await response.Content.ReadAsStringAsync();
...

The first parameter of Timer is a TimerCallback delegate, which should return void
var stateTimer = new Timer(Repeat, autoEvent, 1000, (1000 * 60 * 10));
private void Repeat(object state)
{
....
}

Related

Make methods run asynchronously

Can someone please look at this code and tell me what I am doing wrong. It seems to me that this 3 methods should run in the same same, but they run each after another. Please take look at time writen on console. In my opinion all Console.WriteLine should show ~60ms.
Code sample below:
private async void GetOneCombination(string firstMarket, string secondMarket, string thirdMarket, decimal amountOfFirstCurrency)
{
Stopwatch sw = new Stopwatch();
sw.Start();
Task<GetMarketResponse> result = _accessMethods.GetOrderbook(firstMarket);
Console.WriteLine(sw.ElapsedMilliseconds); // ~60ms
Task<GetMarketResponse> result1 = _accessMethods.GetOrderbook(secondMarket);
Console.WriteLine(sw.ElapsedMilliseconds); // ~130 ms
Task<GetMarketResponse> result2 = _accessMethods.GetOrderbook(thirdMarket);
Console.WriteLine(sw.ElapsedMilliseconds); // ~200 ms
var getMarketResponses = await Task.WhenAll(result, result1, result2);
}
Edit:
To be hosnest I thought that it don`t matter whats inside this methods, i thought that no matter what is done inside it will be done 3 times at the same time
public async Task<GetMarketResponse> GetOrderbook(string market = "USD")
{
var address = AddressBook._orderbook + market;
var response = MethodExecutionTimeMeasurer.Invoke(() =>
_client.ExecuteGetAsyncPublic<GetMarketResponse>(address), out timespan);
_logger.LogInformation(string.Format("OrderBook requested for [{0}], response message: {1}. Time[ms]:{2}",
address,
response.Status,
timespan));
return response;
}
and ExecuteGetAsyncPublic:
public async Task<T> ExecuteGetAsyncPublic<T>(string method)
where T : IBasicResponse
{
var response = await _httpClient.GetAsync(method).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
var responseData = JsonConvert.DeserializeObject<T>(json);
return responseData;
}
MethodExecutionTimeMeasurer
public static class MethodExecutionTimeMeasurer
{
public static T Invoke<T>(Func<Task<T>> action, out TimeSpan timeSpan)
{
var timer = Stopwatch.StartNew();
var res = action.Invoke();
res.Wait();
timer.Stop();
timeSpan = timer.Elapsed;
return res.Result;
}
public static void Invoke(Action action, out TimeSpan timeSpan)
{
var timer = Stopwatch.StartNew();
action.Invoke();
timer.Stop();
timeSpan = timer.Elapsed;
}
}
There are two problems here:
The GetOrderbook method has an asynchronous signature, but its implementation is synchronous. You are probably getting a warning for the async method lacking an await operator.
The MethodExecutionTimeMeasurer.Invoke has a parameter Func<Task<T>> action (an asynchronous delegate), but the created Task is waited synchronously with the Wait method. So during the task's execution, the current thread is blocked.
Each of the three _accessMethods.GetOrderbook invocations returns a completed task, then the combined task Task.WhenAll(result, result1, result2) is also completed upon creation, and in short from the current thread's perspective nothing is running asynchronously. This case is very similar with a question that was asked yesterday, check it out.
Calling an async Task method does not immediately start on a new thread. It will run on the thread that it was called on until it encounters an await.
so for example
var task = DoSomething();
public async Task DoSomething()
{
// MAIN THREAD
await Task.Delay(1);
// WORKER THREAD
}
If you do it like this it will probably work
public async Task<GetMarketResponse> GetOrderbook(string market = "USD")
{
await Task.Delay(1);
var address = AddressBook._orderbook + market;
var response = MethodExecutionTimeMeasurer.Invoke(() =>
_client.ExecuteGetAsyncPublic<GetMarketResponse>(address), out timespan);
_logger.LogInformation(string.Format("OrderBook requested for [{0}], response message: {1}. Time[ms]:{2}",
address,
response.Status,
timespan));
return response;
}
Another approach you can take is
Parallel.Invoke(
() => _accessMethods.GetOrderbook(firstMarket).Wait(),
() => _accessMethods.GetOrderbook(secondMarket).Wait(),
() => _accessMethods.GetOrderbook(thirdMarket).Wait(),
);

Why is SemaphoreSlim not releasing in async Task?

I need to call an API thousands of times as quickly as possible. The API has a limit of 10 calls per second. In order to take full advantage of the 10 calls per second without going over, I'm calling the API asynchronously and throttling the calls with a semaphore and a timer. My code enters the semaphore, calls the API, and then makes sure at least one second has passed before it releases the semaphore.
The API call is actually pretty quick and returns in about a second or less, so my code should move right on to the check time/release semaphore logic. However, what actually happens is after 10 calls the semaphore fills up and is not released at all until the rest of the async Tasks to call the API are created. After that everything works as expected, so I'm not experiencing any real issues. The behavior just seems strange and I would like to understand it.
public static class MyClass
{
SemaphoreSlim semaphore = new SemaphoreSlim(10);
public static async Task CreateCallApiTasks(IList<object> requests)
{
var callApiTasks = requests.Select(x => CallApi(x));
await Task.WhenAll(callApiTasks);
}
private static async Task CallApi(object requestBody)
{
using (var request = new HttpRequestMessage(HttpMethod.Post, <apiUri>))
{
request.Content = new StringContent(requestBody, System.Text.Encoding.UTF8, "application/json");
HttpResponseMessage response = null;
using (var httpClient = new HttpClient())
{
var throttle = new Stopwatch();
ExceptionDispatchInfo capturedException = null;
await semaphore.WaitAsync();
try
{
throttle.Start();
response = await httpClient.SendAsync(request);
while (throttle.ElapsedMilliseconds < 1000)
{
await Task.Delay(1);
}
semaphore.Release();
throttle.Stop();
}
catch (Exception ex)
{
capturedException = ExceptionDispatchInfo.Capture(ex);
}
if (capturedException != null)
{
while (throttle.ElapsedMilliseconds < 1000)
{
await Task.Delay(1);
}
semaphore.Release();
throttle.Stop();
capturedException.Throw();
}
}
}
}
}

Get result of Func<Task<IResponse>> inside timeout check

I'm kinda new at async programming so don't uderstand many things. Please, I need a little bit more help. I did as was recommened in my other question.
public async Task<TResponse> SendRequestAsync<TResponse>(Func<Task<TResponse>> sendAsync)
{
int timeout = 15;
if (await Task.WhenAny(sendAsync, Task.Delay(timeout) == sendAsync))
{
return await sendAsync();
}
else
{
throw new Exception("time out!!!");
}
}
But I need to get a result of sendAsync() and return it. So have I questions:
1) What the best way to do that and how to use Task.Delay with Func<Task<TResponse>>(or may be something instead of it)? I can't figure out how convert(or something) Func to Task.
2) It seems that return await sendAsync() inside if permorms request once more. It is not great. Can I get result of my Func<Task<..>> inside if somehow?
Since you are new in async programming - it's better to not put too much stuff in one statement and better split that:
public async Task<TResponse> SendRequestAsync<TResponse>(Func<Task<TResponse>> sendAsync) {
int timeout = 15;
// here you create Task which represents ongoing request
var sendTask = sendAsync();
// Task which will complete after specified amount of time, in milliseconds
// which means your timeout should be 15000 (for 15 seconds), not 15
var delay = Task.Delay(timeout);
// wait for any of those tasks to complete, returns task that completed first
var taskThatCompletedFirst = await Task.WhenAny(sendTask, delay);
if (taskThatCompletedFirst == sendTask) {
// if that's our task and not "delay" task - we are fine
// await it so that all exceptions if any are thrown here
// this will _not_ cause it to execute once again
return await sendTask;
}
else {
// "delay" task completed first, which means 15 seconds has passed
// but our request has not been completed
throw new Exception("time out!!!");
}
}
Request is sent twice because sendAsync is a Func returning Task, different on each call. You call it first under Task.WhenAny() and repeat in operator return await sendAsync().
To avoid this duplicated call you should save a task to variable and pass that task to both calls:
public async Task<TResponse> SendRequestAsync<TResponse>(Func<Task<TResponse>> sendAsync)
{
int timeout = 15;
var task = sendAsync();
if (await Task.WhenAny(task, Task.Delay(timeout) == task))
{
return await task;
}
else
{
throw new Exception("time out!!!");
}
}
await on completed task will just return its result without rerunning the task.

Access results in Enumerable.Range without having to wait for all tasks to complete

I have a console app, where I need to access some url 200 times, wait for all of the requests to return and work on the 200 results.
I did it like that, in parallel:
var classNameTasks = Enumerable.Range(1, 200).Select(i => webApi.getSplittedClassName()).ToArray();
string[][] splittedClassNames = await Task.WhenAll(classNameTasks);
if (splittedClassNames[0] == null)
result = new TextResult("Error accessing the web");
getSplittedClassName returns a string[], if the internet is off it will return null.
Now, as you can see, after the completion of all the tasks, I do an if to check the content, if its null -> internet issues.
The problem here is that I need to wait for the whole 200 requests to return before I can check the content.
I am looking for a way to right away detect a scenario where there is no internet, and I return null, without having to wait for the 200 requests.
To do this you need
A CancellationTokenSource to signal that the job is done.
The WhenAll method from Tortuga.Anchor
static async Task Test()
{
TextResult result;
var cts = new CancellationTokenSource();
var classNameTasks = Enumerable.Range(1, 200).Select(i => getSplittedClassName(cts)).ToArray();
await classNameTasks.WhenAll(cts.Token);
if (cts.IsCancellationRequested)
result = new TextResult("Error accessing the web");
string[][] splittedClassNames = classNameTasks.Select(t => t.Result).ToArray();
}
private static async Task<string[]> getSplittedClassName(CancellationTokenSource cts)
{
try
{
//real code goes here
await Task.Delay(1000, cts.Token); //the token would be passed to the real web method
return new string[0];
}
catch
{
cts.Cancel(); //stop trying
return null;
}
}

How to cancel other threads as soon as one completed thread satisfies condition

I have an ASP.NET MVC application which needs to check if something exists at 3 remote API servers. The application passes an ID to each API and it returns either true or false. The code looks like this.
public class PingController
{
public async Task<bool> IsFound(int id)
{
var servers = new ['a.com', b.com', 'c.com'];
var result = await foundAtServers(id, servers);
return result;
}
private async Task<bool> foundAtServers(int id, string[] servers)
{
var tasks = from server in servers
select checkServer(id, server);
return await.Task.WhenAll(tasks.ToArray());
}
private async Task<bool> checkServer(id, server)
{
var request = new HttpRequestMessage(HttpMethod.Get, server+"/api/exists"+id);
var client = new HttpClient();
var task = await client.SendAsync(request);
var response = await task.Content.ReadAsStringAsync();
return bool.Parse(response);
}
}
This code currently checks all 3 APIs asynchronously but will wait until ALL of the HttpClient calls have completed before the MVC Action can return.
As soon as one API returns true I want to immediately return true on the Action, rather than wait for the other tasks to complete.
The C# Task class has .WaitAll and .WaitAny, but these won't work either. As I need to cancel the other HttpClient request, I presume I need to use a CancellationToken but I don't know how to use it with this structure.
Cheers.
If you want to immediately return, you can use Task.WhenAny instead of Task.WhenAll. This won't cancel the on-going tasks, but it will enable you to return as soon as possible:
private async Task<bool> FoundAtServersAsync(int id, string[] servers)
{
var tasks = (from server in servers
select checkServer(id, server)).ToList();
while (tasks.Count > 0)
{
var finishedTask = await Task.WhenAny(tasks);
if (finishedTask.Result)
{
return finishedTask.Result;
}
tasks.Remove(finishedTask);
}
return false;
}
This will discard the other tasks. This means that if any exception is thrown inside one of them, it will be swallowed.
Edit:
If you care about actually canceling the other tasks, consider passing your CancellationToken to the overload of SendAsync which takes one, and calling CancellationTokenSource.Cancel once a value is received. Note this will mean you'll also need to handle the OperationCanceledException they will throw.
If they don't matter, i'd simply discard them as above.
This problem is made easier by using the following method to take a sequence of tasks and order them based on when they are completed.
public static IEnumerable<Task<T>> Order<T>(this IEnumerable<Task<T>> tasks)
{
var taskList = tasks.ToList();
var taskSources = new BlockingCollection<TaskCompletionSource<T>>();
var taskSourceList = new List<TaskCompletionSource<T>>(taskList.Count);
foreach (var task in taskList)
{
var newSource = new TaskCompletionSource<T>();
taskSources.Add(newSource);
taskSourceList.Add(newSource);
task.ContinueWith(t =>
{
var source = taskSources.Take();
if (t.IsCanceled)
source.TrySetCanceled();
else if (t.IsFaulted)
source.TrySetException(t.Exception.InnerExceptions);
else if (t.IsCompleted)
source.TrySetResult(t.Result);
}, CancellationToken.None,
TaskContinuationOptions.PreferFairness,
TaskScheduler.Default);
}
return taskSourceList.Select(tcs => tcs.Task);
}
With this you can write:
public static async Task<bool> WhenAny(this IEnumerable<Task<bool>> tasks)
{
foreach (var task in tasks.Order())
if (await task)
return true;
return false;
}
You could wait for the first task to complete - if it's successful, return true immediately. Otherwise, wait for the next one to complete, and so on and so forth.
private async Task<bool> foundAtServers(int id, string[] servers)
{
var tasks = servers.Select(server => checkServer(id, server))
.ToList();
while(tasks.Any())
{
var task = await Task.WhenAny(tasks);
if(task.Result)
return true;
tasks.Remove(task);
}
return false;
}

Categories