I've a web API on server side as below. It is using async-await pattern. All these methods are present inside MyClassController class
[Route("install")]
public async Task<IHttpActionResult> InstallProduct()
{
Log("milestone1");
Install();
Log("milestone6");
}
private bool Install()
{
MyClass.Register();
Log("milestone5");
}
private static async void Register()
{
Log("milestone2");
using (HttpClientHandler handler = new HttpClientHandler())
{
using (var client = new HttpClient(handler))
{
var method = "http://BaseUrlOfWebsite#2/api/applications";
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
//make POST API call into website # 2
var response = await client.PostAsync(method, form);
Log("milestone3");
}
}
}
Log("milestone4");
}
Overall call is like this:
Client code -> install API on website # 1 -> applications API on website # 2
My expected log statements are in below order:
milestone1
milestone2
milestone3
milestone4
milestone5
milestone6
Actual log order that I see in log files is as below:
milestone1
milestone2
milestone5
milestone6
milestone3
milestone4
Code doesn't causes error anywhere but it returns 500 internal server error code to calling client. I've two questions here:
Why the logs are not as per my expectation? My understanding is that Register method should get blocked while calling external website as I'm using await while waiting for response.
Can this out of order code execution cause 500 internal server error even though my code isn't throwing any error?
Note: Same logs get printed in expected order when I debug the install API with debugger attached in Visual Studio.
Update: If I make the call to external website client.PostAsync run synchronously forcefully using Task.Run then I get 200 OK response. Why web API results in error if I can fire and forget the call to external website and my remaining code can run synchronously to finish the web API call.
Register method is async, it returns void, instead of Task, which could be awaited.
And instead of awaiting for task your code is running synchronously.
[Route("install")]
public async Task<IHttpActionResult> InstallProduct()
{
Log("milestone1");
await Install();
Log("milestone6");
}
private async Task Install()
{
await MyClass.Register();
Log("milestone5");
}
private static async Task Register()
{
Log("milestone2");
using (var handler = new HttpClientHandler())
{
using (var client = new HttpClient(handler))
{
var method = "http://BaseUrlOfWebsite#2/api/applications";
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
//make POST API call into website # 2
var response = await client.PostAsync(method, form);
Log("milestone3");
}
}
}
Log("milestone4");
}
In general, when you see async void in your code it’s bad news, because:
you can’t wait for its completion (as mentioned in this post already)
any unhandled exceptions will terminate your process (ouch!)
Suppose you have a timer event that fires every once in a while and you want to do some asynchronous processing inside the handler, so you proceed to write something along the lines of:
var timer = new Timer();
timer.Elapse += OnTimerFired;
timer.Interval = 1000;
timer.Start();
...
private static async void Register()
{
await Task.Delay(1000);
// this is going to terminate your process!
throw new Exception();
}
Related
I'm newbie in async. And making WPF app for scraping and API calls purposes. WPF's UI is needed only for service monitoring and settings control, so all services will run simultaneously in the background. And most of them is doing similar work.
For this one I need to implement strategy like this:
Start worker on threadpool
Worker must send request and process response from Website
2.1 If response processed and some new data appeared - raise an event
2.2 If request failed - handle an error
2.3 If there are many error percent for last x requests - stop worker
No matter the last request completed/failed/running we must send another request
Another request should be sent not earlier than the set delay but should not exceed the delay too much (as far as possible).
private _workTask;
private List<ScrapeParameters> _scrapeParams = new();
public event EventHandler<ScrapedEventArgs>? NewDataScraped;
//Can I run Worker like this?
public void RunWorker()
{
if(_workTask.IsCompleted)
_workTask = WorkAsync(_token)
}
private async Task WorkAsync(CancellationToken cancelToken)
{
List<Task> processTasks = new();
while(true)
{
if(cancelToken.IsCancellationRequested) return;
//Delay could be from 0.5 second to any value
var delayTask = Task.Delay(WorkerDelay);
var completedTasks = processTasks.Where(t => t.IsCompleted)
var setToHandle = new HashSet<Task>(completedTasks);
foreach(var task in setToHandle)
{
//Theoretical logic to handle errors and completion
if(task.IsFaulted)
HandleFaultedTask(task);
else
CountCompleted();
processTasks.Remove(task);
}
//Theoretical logic to obtain the desired parameters.
var currParameters = GetParameters();
processTasks.Add(ProcessAsync(currParameters, cancelToken));
await delayTask;
}
}
//This method usually takes around 2-4 seconds
private async Task ProcessAsync(ScrapeParameters parameters CancellationToken cancelToken)
{
//Some work with http requests
var response = await Client.GetAsync(parameters.ToUri());
...
//Processing response
...
if(newData != null)
NewDataScraped?.Invoke(new(newData));
}
Does my implementation matches the TAP pattern?
Especially I would like to focus on RunWorker() and setToHandle
I have an API in c# (MYAPI) which takes a request, and makes an API call to Unity (UNITYAPI) to perform some action.
The flow should be like this
Client -> MYAPI -> UnityAPI -> performs task -> response from UnityAPI to MYAPI -> response to client.
Now, A client can call MYAPI through different threads. MYAPI should wait for the previous MYAPI calls to complete (which in turn means that the UNITYAPI completed some action). MYAPI "CANNOT" send any request to UNITYAPI until the previous calls of MYAPI are responed back to the client.
Function in MYAPI to make api calls to UNITY API:
static async Task<string> PostURI(HttpContent c)
{
Uri uri = new Uri("http://localhost:4444");
var response = string.Empty;
using (var client = new HttpClient())
{
HttpResponseMessage result = null;
try
{
result = await client.PostAsync(uri, c);
}
catch (Exception ex)
{
throw ex;
}
if (result.IsSuccessStatusCode)
{
response = result.StatusCode.ToString();
}
}
return response;
}
Function in MYAPI which handles all the calls:
public void ProcessCalls(string operation)
{
HttpContent next = new StringContent(operation);
while (isBlocked == true)
{
}
isBlocked = true;
var t = Task.Run(() => PostURI(next));
t.Wait();
isBlocked = false;
}
Here, you can see the workaround I did. I just used a static variable isBlocked to make any call wait in an infinite while loop until any other operation is being done.
This works fine when I send 2 parallel calls to MYAPI. The second call waits for the first to complete, then proceeds to send call to UNITYAPI and waits. But when I send 3 calls in parallel, it breaks. The first call completes in unity but no response is received for any call. There must be an elegant way to do this.
So what i want is:
-The client should be able to send multiple requests to MYAPI.
-Each request should be made to wait till ALL the previous requests have sent a response back to client.
Thanks in advance.
EDIT 1: The method which calls ProcessCalls:
[HttpPost]
[Route("MyPostRoute")]
public async void MyPostRoute()
{
var request = new StreamReader(Request.Body).ReadToEnd();
// Doing some validations on the request
if (request.isValid())
{
await helperobject.ProcessCalls(request);
//helperobject is an object of the class which has the two functions mentioned above.
}
else
throw new Exception("Input was not in Correct format");
}
Using a Semaphore, you need to make it static, so all instances of that class use the same one. Then you can go like this:
private static SemaphoreSlim processCallsSemaphore = new SemaphoreSlim(1,1);
// new SemaphoreSlim(1,1); => start with 1 count available and allow at max 1.
public async Task ProcessCalls(string operation)
{
HttpContent next = new StringContent(operation);
try
{
await processCallsSemaphore.WaitAsync();
await PostURI(next); // Assuming this is also an async API or can be made one.
}
finally
{
processCallsSemaphore.Release();
}
}
I'm basing myself on this sample:
async-example-with-HttpClient
I have the exact same code, which I'm running from a test:
public async Task<IEnumerable<Stuff>> GetStuff()
{
var r = await DownloadPage("http://stackoverflow.com");
}
static async Task<string> DownloadPage(string url)
{
using (var client = new HttpClient())
{
using (var r = await client.GetAsync(new Uri(url)))
{
string result = await r.Content.ReadAsStringAsync();
return result;
}
}
}
But it doesn't work. The debugger just stops on the await client.GetAsync(new Uri(url)) call.
There's no exception being thrown, there's nothing in the Visual Studio output window.
I have Fiddler open with all sessions cleared, I see nothing changing on doing that GET call.
I have nothing to go on why this doesn't work.
Any ideas on why this doesn't work and what I can do?
Update
This does partly work when called from an API, not from the xunit test I was starting with. But, when called from the API the response from the backend isn't received and the caller keeps waiting forever:
If I keep the code entirely on the API side, so no calls to any deeper dependencies, everything works. Context switching must be causing issues somehow.
I am using the .NET 4.5 HttpClient class to make a POST request to a server a number of times. The first 3 calls run quickly, but the fourth time a call to await client.PostAsync(...) is made, it hangs for several seconds before returning the expected response.
using (HttpClient client = new HttpClient())
{
// Prepare query
StringBuilder queryBuilder = new StringBuilder();
queryBuilder.Append("?arg=value");
// Send query
using (var result = await client.PostAsync(BaseUrl + queryBuilder.ToString(),
new StreamContent(streamData)))
{
Stream stream = await result.Content.ReadAsStreamAsync();
return new MyResult(stream);
}
}
The server code is shown below:
HttpListener listener;
void Run()
{
listener.Start();
ThreadPool.QueueUserWorkItem((o) =>
{
while (listener.IsListening)
{
ThreadPool.QueueUserWorkItem((c) =>
{
var context = c as HttpListenerContext;
try
{
// Handle request
}
finally
{
// Always close the stream
context.Response.OutputStream.Close();
}
}, listener.GetContext());
}
});
}
Inserting a debug statement at // Handle request shows that the server code doesn't seem to receive the request as soon as it is sent.
I have already investigated whether it could be a problem with the client not closing the response, meaning that the number of connections the ServicePoint provider allows could be reached. However, I have tried increasing ServicePointManager.MaxServicePoints but this has no effect at all.
I also found this similar question:
.NET HttpClient hangs after several requests (unless Fiddler is active)
I don't believe this is the problem with my code - even changing my code to exactly what is given there didn't fix the problem.
The problem was that there were too many Task instances scheduled to run.
Changing some of the Task.Factory.StartNew calls in my program for tasks which ran for a long time to use the TaskCreationOptions.LongRunning option fixed this. It appears that the task scheduler was waiting for other tasks to finish before it scheduled the request to the server.
Well... I have a Login page and a WCF implanting the Event Based Async pattern. Eventually what I need to do here is to make my async call to the WCF method and the execution should wait for the response before continuing with the authentication or not and then redirect.
I am developing on WP8 and I call my WCF this way:
Login.xaml.cs
private async void SignIn_Click(object sender, EventArgs e)
{
await App.Instance.StaticServiceData.LoadUser(App.Instance.User);
LoginSuccessFail();
}
WCF_StaticDataService.cs
public async Task LoadUser(User user)
{
var client = new BMAStaticDataService.StaticClient();
client.AuthenticateUserAsync(user);
client.AuthenticateUserCompleted += async (o,e) => {
try
{
await UpdateCacheUserData(existing);
if (existing.UserId > 0)
{
App.Instance.User.UserId = existing.UserId;
App.Instance.User.Email = existing.Email;
App.Instance.IsUserAuthenticated = true;
}
else
{
App.Instance.IsUserAuthenticated = false;
throw new Exception("User has no authentication");
}
}
catch (Exception){throw;}
}
I want to call the LoginSuccessFail(); method and then navigate to the appropriate page only after the WCF call is completed.
Note that I wouldn't like to put my navigation aware code in the Service class and the AuthenticateUserCompleted event since it is not its job to handle this.
Eventually I would like to receive a callback or feedback or whatever in my Login.xaml.cs after the service call is done, successfully or not.
Hope my info is enough. Otherwise please advise to provide further clarification.