WP8 Async/Await begingetresponse not waiting, gets run last - c#

I may be misunderstanding the flow of control, because by all accounts this seems like it should work. This is a Windows phone 8 app. I am attempting to make a web request, and accordingly display the data returned. I am trying to get the data (here, called 'Key') in the following method:
public Task<String> getSingleStockQuote(String URI)
{
return Task.Run(() =>
{
String key = null;
HttpWebRequest request = HttpWebRequest.Create(URI) as HttpWebRequest;
HttpWebResponse response;
try
{
request.BeginGetResponse((asyncres) =>
{
HttpWebRequest responseRequest = (HttpWebRequest)asyncres.AsyncState;
response = (HttpWebResponse)responseRequest.EndGetResponse(asyncres);
key = returnStringFromStream(response.GetResponseStream());
System.Diagnostics.Debug.WriteLine(key);
}, request);
}
catch (Exception e)
{
System.Diagnostics.Debug.WriteLine("WebAccessRT getSingleStockQuote threw exception");
key = String.Empty;
}
return key;
});
}
...And I am calling this method like so:
WebAccessRT rt = new WebAccessRT();
await rt.getSingleStockQuote(stockTagURI);
System.Diagnostics.Debug.WriteLine("Past load data");
The WriteLine() in BeginGetResponse is for testing purposes; it prints after "Past Load Data". I want BeginGetResponse to run and complete operation (thus setting the Key), before the task returns. The data prints out right in the console, but not in the desired order - so Key is set and has a value, but its the very last part that gets run. Can someone point me in the right direction and/or see what's causing the problem above? Thinking this through, is the await operator SIMPLY waiting for the Task to return, which is returning after spinning off its async call?

BeginGetResponse starts an asynchronous process (hence the callback) so you cannot guarantee the order it is completed. Remember that the code within BeginGetResponse is actually a separate method (see closures) that is executed separately from getSingleStockQuote. You would need to use await GetResponseAsync or (imo, even better - you could greatly simplify your code) use HttpClient.

Related

WebAPI HTTP request not completing until queued work kicks off on background task

In my .Net 6 WebPI service, I am queueing work to a background task queue, very closely based on the example here, but I could post parts of my implementation if that would help:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks
I am running into unexpected behavior where control is not returned to the caller, even after the return Ok(..) completes in the controller. Instead the request only completes after the await Task.Delay(1000); line is reached on the queued work item. The request returns to the client as soon as this line is reached, and does not need to wait for the Delay to finish.
I'm guessing this is because of the await either starting a new async context, or someone un-sticking the async context of the original request. My intention is for the request to complete immediately after queuing the work item.
Any insight into what is happening here would be greatly appreciated.
Controller:
public async Task<ActionResult> Test()
{
var resultMessage = await _testQueue.DoIt();
return Ok(new { Message = resultMessage });
}
Queueing Work:
public TestAdapter(ITaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
public async Task<string> DoIt()
{
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) =>
{
await Task.Delay(1000);
var y = 12;
});
return "cool";
}
IoC:
services.AddHostedService<QueueHostedService>();
services.AddSingleton<ITTaskQueue>(ctx =>
{
return new TaskQueue(MaxQueueCount);
});
TaskQueue:
private readonly Channel<BackgroundTaskContext> _queue;
public TaskQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<BackgroundTaskContext>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(new BackgroundTaskContext(workItem, ...));
}
Not sure what you expect here. I'm assuming you want the async method to return the cool in the api response. That's fine but because your also awaiting the async call with in DoIt(), then it pauses until QueueBackgroundWorkItemAsync finishes. You could remove the await and it will run and return as you expect.
I can't say that I'm a big fan of that design as you lose contact with it with exception of the cancellation token. Another thought would be to Send that work off to a console job or function app using message bus or even another http call.
Additional Notes:
Async can be complicated to explain because in reality, it wraps up code and executes on a thread of it's choosing. The await simulates the synchronous behavior.
await Task.Delay(1000); // Runs on it's own thread but still halts code execution for 1 second.
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) // Waits for control to be returned from the code inside.
var resultMessage = await _testQueue.DoIt(); // Always waits for the code inside to complete.
If your wanting something to run without pausing code execution, you can either remove the await or add a Task.Run(() => { }); pattern. Is it a good idea is a whole other question. It also matters whether you need information back from the async method. If you don't await it then you'll get null back as it doesn't wait around for the answer to be computed.
This appears just to have been user error using the debugger. The debugger is switching to the background task thread and hitting breakpoints there before the response fully returns giving the appearance that control was not being returned to the client and being carried into the background task.
Even after adding some synchronous steps in QueueBackgroundWorkItemAsync and putting breakpoints on them, control was not immediately returned to the original http call. Only after I tried adding a long running task like await Task.Delay(1000); did enough time/ticks pass for the http response to return. I had conflated this with just the await somehow freeing up the original http context.

Why can I not await the return value from my async method?

Situation
I have a raspberry pi set up as a server taking json via HTTP as input.
The "API" allows to set leds connected to the pi. That all works, I can send requests from the browser and everything is great.
It takes a while for the response to arrive. That's why I want to communicate asynchrounously.
I found this on msdn that explains how it's done.
// Three things to note in the signature:
// - The method has an async modifier.
// - The return type is Task or Task<T>. (See "Return Types" section.)
// Here, it is Task<int> because the return statement returns an integer.
// - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{
// You need to add a reference to System.Net.Http to declare client.
HttpClient client = new HttpClient();
// GetStringAsync returns a Task<string>. That means that when you await the
// task you'll get a string (urlContents).
Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");
// You can do work here that doesn't rely on the string from GetStringAsync.
DoIndependentWork();
// The await operator suspends AccessTheWebAsync.
// - AccessTheWebAsync can't continue until getStringTask is complete.
// - Meanwhile, control returns to the caller of AccessTheWebAsync.
// - Control resumes here when getStringTask is complete.
// - The await operator then retrieves the string result from getStringTask.
string urlContents = await getStringTask;
// The return statement specifies an integer result.
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
return urlContents.Length;
}
For the top level overview, here's how my Main method looks like (it doesn't compile):
var pi1 = new RaspberryPi(#"http://192.168.0.100:8080"); // specify IP
var led = new Led(255, 100, 180); // r, g, b values wrapped in an Led object
Led result = await pi1.setLedAsync(2, led); // FAIL // what should be an async POST, awaiting the response
I hope that makes sense.
The Led class is just a data object holding 3 bytes for the 3 channels and some conversion methods to and from json.
The setLedAsync method:
async public Task<Led> setLedAsync(uint n, Led led)
{
var client = new HttpClient();
client.BaseAddress = _uri;
var content = new StringContent(led.ToJson(), Encoding.UTF8, "application/json");
Task<HttpResponseMessage> response = client.PutAsync("/led/" + n, content);
HttpResponseMessage responseMessage = await response;
string json = await responseMessage.Content.ReadAsStringAsync();
return new Led(json);
}
Error
This line is where I get an error for using await:
Led result = await pi1.setLedAsync(2, led);
await can only be used in an async method.
Questions
Why do I get this error? The last comment line in the example code
// Any methods that are awaiting AccessTheWebAsync retrieve the length value.
makes me think that this is how it should be done. As I understand it, the await basically unwrapps the Task<T> into a T.
If I do not use await, I get a type missmatch, because the method returns Task<Led>, not Led.
What's confusing for me is to understand the difference between the example and my situation. I have to use await twice in my async method:
HttpResponseMessage responseMessage = await response;
string json = await responseMessage.Content.ReadAsStringAsync();
The thing is that I have to deal with this HttpResponseMessage as a middleman. I suspect that I'm prematurely giving up the asynchronousity with this second await somehow (if that makes any sense) I think this is the origin of the problem, but I'm not sure how to solve it.
Edit
I wrapped the function call in an asyn method, which allows to compile the code.
But it's not asynchronous. I added a delay on the server side to test this.
class Program
{
static void Main(string[] args)
{
var prog = new Program();
Console.WriteLine("before init");
prog.init();
Console.WriteLine("after init"); // not happening before the response arrives
Console.Read();
}
private async void init()
{
var pi1 = new RaspberryPi(#"http://192.168.0.100:8080"); // specify IP
var led = new Led(255, 0, 0); // r, g, b values wrapped in an Led object
Console.WriteLine("before await");
Led result = await pi1.setLedAsync(2, led); // what should be an async POST, awaiting the response
Console.WriteLine("after await");
}
}
None of the "after" messages are written to the console before the response from the request arrives.
You get the error because an asynchronous wait -- an await -- implies that the method doing the awaiting is itself asynchronous. I think you do not understand what await means. Await does not mean synchronously block until the result is available -- that is the opposite of what it means. Await means return immediately so my caller can do important work without waiting for this result; schedule the remainder of this method at some time in the future when the result is available. Await is an asynchronous wait. When you await a letter to arrive in the mail you do not sit by the door doing nothing until it arrives; you do other work asynchronously and then resume the task of reading your mail at some time after the letter arrives.
You say that the method -- the method doing the awaiting, not the method returning the task being awaited -- is Main. If this is a console app then you cannot make Main asynchronous because when Main returns the program ends. Again, internalize this: an await is just a return from the perspective of the current method. The current method will be called again later in the future and will pick up where it left off, but when Main returns there is no future. So you cannot make Main async.
You note that async turns a task of T into a T, which is correct, but it does so asynchronously. So your principal choices here are here are:
turn the task of T into a T synchronously from Main
write some sort of app other than a Console app; say, a winforms or WPF app, which does not terminate until you tell it to

Get host entry async freezes

I have a problem that occurs when i have deployed the code to the production server. It does not happen locally on my computer. When i run the line
var host = await Dns.GetHostEntryAsync(adresse);
The production environment freezes or awaits indefinitely. Why does this happen?
Here is the code:
public async Task<string> HentAsync(string ip)
{
if (string.IsNullOrWhiteSpace(ip) || kLoopbackIp.Contains(ip))
{
return string.Empty;
}
if (IpHostname.ContainsKey(ip))
{
UpdateAndRemoveOldElements(ip);
return IpHostname[ip].Item1;
}
try
{
IPAddress adresse = IPAddress.None;
if (!IPAddress.TryParse(ip, out adresse))
{
return string.Empty;
}
var host = await Dns.GetHostEntryAsync(adresse);
string hostname = host.HostName;
IpHostname.AddOrUpdate(ip,
new Tuple<string, DateTime>(hostname, DateTime.Now),
(x, y) => new Tuple<string, DateTime>(hostname, DateTime.Now));
return hostname;
}
catch (Exception)
{
// Tar i mot exception og returnerer tom string
return string.Empty;
}
}
UPDATE:
The async method is called like this from a sync method that is doing the same thing (could this be the culprit).
public string Hent(string ip)
{
return HentAsync(ip).Result;
}
The most likely cause is that the origin of this call is running on the UI thread; perhaps the call to Hent() updates a UI element?
The problem is well documented here: await works but calling task.Result hangs/deadlocks
Hent() calls HentAsync() and then await Dns.GetHostEntryAsync(). This last call spawns a new thread, but wants to return to the calling thread when it has finished, which in this case is the UI thread, but it cannot do this because .Result is squatting on the thread rather than yielding it temporarily like it would have with await.
As #spender and #Dirk point out, one solution is to add .ConfigureAwait(false) to await Dns.GetHostEntryAsync(). In fact you should add this to every await where you don't care what thread context the function continues in.
This approach is still quite fragile though, as any time you forget to do it or execution takes a path with an unmodified await, the problem will happen again. The best option is to make the top-level UI-threaded function async and then await every function call that could cause a delay. This is the canonical use-case for async / await and leads to flat, easy to follow code with very little chance of deadlock.

WP7 synchronous web request

I’m developing a Windows Phone 7.1 application, and trying to implement tombstoning.
Due to the legal reasons I can’t save my view model. I’m only saving encrypted session ID, which can be used to load a view model data from the remote server.
On resume, I need to verify the session ID, if it’s expired – I take user to the login page of my app, if it’s still OK, I reload view model data from the server.
The problem is the HttpWebRequest lacks blocking API. Moreover, while inside page.OnNavigatedTo method after de-tombstoning, the method described here blocks forever.
I’ve worked around the problem by presenting my own splash screen.
However, I’d rather like to complete those RPC calls while the system-provided “Resuming…” splash screen is visible, i.e. before I return from page.OnNavigatedTo method.
Any ideas how can I complete HTTP requests synchronously while inside page.OnNavigatedTo after de-tombstoning?
Let me start out by saying that Microsoft really tries to push you to do async calls for good reasons, which is why I wanted to emphasize it.
Now if you really want to do it synchronous, I have an idea which I haven't been able to test myself. When using the HttpWebRequest class, there are two important functions, which you've probably used as well: BeginGetResponse and EndGetResponse.
These two functions work closely together. BeginGetResponse starts a asynchronous webrequest, where when the request is finished the EndGetResponse gives you to ouput when it's done. This is the way MS tries to let you do it. The trick to doing this stuff synchronously is that the beginGetResponse returns a IAsyncResult. This IAsyncResult interface contains a WaitHandler, which can be used to synchronously wait until the request is done. After which you can just continue with the endGetRequest and go on with your bussiness. The same thing goes for the BeginGetRequestStream and EndGetRequestStream.
But as I said before, I haven't tested this solution and it's purely theoretical. Let me know if it worked or not.
Good luck!
Update: another option is to use Reactive Extensions.
If you're on VS2010 you can install the AsyncCTP and when you do an extension method gets added that allows you to await the response.
static async Task<Stream> AsynchronousDownload(string url)
{
WebRequest request = WebRequest.Create(url);
WebResponse response = await request.GetResponseAsync();
return (response.GetResponseStream());
}
then:
UPDATED:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
var myResponse = await AsynchronousDownload("http://stackoverflow.com");
}
or
If you're using VS2012 you can install the Microsoft.Bcl.Async lib and do the same thing as if you were using the AsyncCTP, await the response.
or
You could implement something similar to Coroutines in Caliburn Micro. For this you implement the IResult interface.
public interface IResult
{
void Execute(ActionExecutionContext context);
event EventHandler<ResultCompletionEventArgs> Completed;
}
A possible implementation:
public class HttpWebRequestResult : IResult
{
public HttpWebRequest HttpWebRequest { get; set; }
public string Result { get; set; }
public HttpWebRequestResult(string url)
{
HttpWebRequest = (HttpWebRequest) HttpWebRequest.Create(url);
}
public void Execute (ActionExecutionContext context)
{
HttpWebRequest.BeginGetResponse (Callback, HttpWebRequest);
}
public void Callback (IAsyncResult asyncResult)
{
var httpWebRequest = (HttpWebRequest)asyncResult.AsyncState;
var httpWebResponse = (HttpWebResponse) httpWebRequest.EndGetResponse(asyncResult);
using (var reader = new StreamReader(httpWebResponse.GetResponseStream()))
Result = reader.ReadToEnd();
Completed (this, new ResultCompletionEventArgs ());
}
public event EventHandler<ResultCompletionEventArgs> Completed = delegate { };
}
Then to call it:
var httpWebRequestResult = new HttpWebRequestResult("http://www.google.com");
yield return httpWebRequestResult;
var result = httpWebRequestResult.Result;
This might be an example of grabbing the Coroutines implementation from CM and using it separately.

BeginGetResponse in web application

I want to use the BeginGetResponse method in order to call many URLs which I hold on a list.
I have 2 questions on how to implement this:
according to the example in http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.begingetresponse(v=vs.95).aspx
we use:
public static ManualResetEvent allDone= new ManualResetEvent(false);
is that wise to use a static member in web application since it's shared with other threads ? can this cause for problems ?
how can I tell when all the callbacks were finished ? I need to do a summary report on the results
Thanks
While you can use an event, I'd recommend using Task<T> and the FromAsync method on the TaskFactory class like so:
// Execution of tasks starts here because of the
// call to ToArray.
Task<WebResponse>[] tasks = uris.Select(u => {
// Create the request.
WebRequest req = ...;
// Make the call to return the response asynchronously with
// a Task.
return Task.Factory.FromAsync(req.BeginGetResponse,
req.EndGetResponse, null);
}).ToArray();
Once you have that, you can easily wait on all of the Task<T> instances using the ContinueWhenAll method on the TaskFactory class like so with a continuation:
Task.Factory.ContinueWhenAll(tasks, t => {
// Note that t is an array of Task, so you have to cast
// each element to a Task<WebRequest>.
// Process all of them here.
});
Note the above returns a Task which you will have to wait on or continued on when done (if your're concerned about the notification).
If you are using .NET 4.5, you don't need to use the ContinueWhenAll method on the TaskFactory class, but can use the WhenAll method on the Task class to perform the work:
// Note that work does not start here yet because of deferred execution.
// If you want it to start here, you can call ToArray like above.
IEnumerable<Task<WebResponse>> tasks = uris.Select(u => {
// Create the request.
WebRequest req = ...;
// Make the call to return the response asynchronously with
// a Task.
return Task.Factory.FromAsync(req.BeginGetResponse,
req.EndGetResponse, null);
});
// Execution will start at this call:
Task<Task<WebRequest>[]> allTasks = Task.WhenAll(tasks);
// Continue or wait here.
Note that the above was before it was revealed that .NET 3.5 was being used.
If you want to wait in main thread for completition, then this solution is not very good. First request will change event's state to "set". Therefore, main thread will continue execution after the first request completed.
A suggest you to use CountdownEvent:
using(var countdownEvent = new CountdownEvent(list.Count))
{
// launch requests with countdownEvent.Signal(); in the end
countdownEvent.Wait();
}
You must store reference to countdownEvent inside RequestState. Also, don't forget to control timeouts - start new thread with ThreadPool.RegisterWaitForSingleObject.
I guess you're trying to do something like this:
int total = urls.Count;
ManualResetEvent evt = new ManualResetEvent();
ConcurrentBag<WebResponses> responses = new ConcurrentBag<WebResponse>();
foreach(Uri in uri)
{
HttpWebRequest req = ...;
req.BeginGetResponse(res=>
{
WebResponse res = req.EndGetResponse();
// do what you need with the response.
// maybe add it to a collection so you can report on it later:
responses.Add(res);
if(Interlocked.Decrement(ref total) == 0)
{
// this was the last response. set event.
evt.Set();
}
}, null);
}
evt.Wait();
foreach(WebResponse res in responses)
{
// report something about the response.
}
Note that an optimal workflow will not need an event. For extra credit, get rid of it all together and move your final logic inside the if that sets the event.
Also, this code is untested and lacks error handling, so be sure to add that in if you play with it at all.

Categories