In my WCF service, I have to make a call to an API, where I wanted to do a Fire and Forget implementation. And If possible just capture the errors if any.(That's fine too , if not an option)
I am planning to do the following implementation, what are the issues it could lead to? By doing the following implementation is going to leave a huge number of open connections. Or what could be the issue? Please help in understanding how in a better way this can be implemented.
void SendRequest(inputs)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(URL);
request.Method = "POST";
request.ContentType = "application/xml";
byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);
using (Stream requestStream = request.GetRequestStream())
{
requestStream.Write(requestBytes, 0, requestBytes.Length);
}
request.GetResponseAsync();
}
Main()
{
try
SendRequest(inputs);
catch ex
log ex;
}
First, make fully async version of your code
using System.Threading;
public async Task<System.Net.WebResponse> SendRequestAsync(
string inputXML, string url, CancellationToken cancellationToken)
{
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Method = "POST";
request.ContentType = "application/xml";
byte[] requestBytes = Encoding.UTF8.GetBytes(inputXML);
// GetRequestStreamAsync is a lightweight operation I assume
// so we don't really need any cancellation
using (Stream requestStream = await request.GetRequestStreamAsync())
{
// Idk if we really need cancellation here, but just in case of big request we might need to
await requestStream.WriteAsync(
requestBytes, 0, requestBytes.Length, cancellationToken);
}
// Close any long-running request
using (cancellationToken.Register(() => request.Abort(), useSynchronizationContext: false))
{
var response = await request.GetResponseAsync();
cancellationToken.ThrowIfCancellationRequested();
return response;
}
}
Let's create an async void method, but make it safe. It will basically execute in "fire-and-forget" manner.
public async void DoWork(string inputXML, string url, CancellationToken ct)
{
try
{
using(var response = await SendRequestAsync(inputXML, url, ct))
{
var httpResponse = (HttpWebResponse) response;
// Use 201 Created or whatever you need
if (httpResponse.StatusCode != HttpStatusCode.Created)
{
// TODO: handle wrong status code
}
}
}
catch (Exception e)
{
if (ct.IsCancellationRequested)
{
Console.WriteLine("Cancelled");
}
else
{
// TODO: handle exception
}
}
}
private static CancellationTokenSource _cts = new CancellationTokenSource();
public static void Main (string[] args)
{
DoWork("xml string", "example.com", cts.Token);
Console.WriteLine("Boom!");
if (Console.ReadLine() == "exit")
{
// Cancel the damn job
cts.Cancel();
}
}
It's important to handle all errors from inside a DoWork, because following will not work
// Warning, will NOT catch an exception
public static void Main (string[] args)
{
try
{
DoWork("xml string", "example.com");
}
catch (Exception e)
{
}
}
EDIT: OP requested cancellation so I added cancellation
Please note that it's not best practice not to use fire and forget, especially if this a core layer of the application and you might miss important exceptions. When you use this technique you have to remember that the following happens:
Exception will be fail silently without any chance of catching them. normally you will want to log them or get a notification.
You have no idea when the code completes,
Since You don't need the code to complete and it might may not run to you would have no notification that it failed to complete.
A good case scenario for using this technique could be for updating a cache for an example.
Having said that, you could use the following techniques:
NET 4.5 allows us to use it via Task.Run
Task.Run(() => FireAndForget());
You could also start a thread with parameterless lambda:
(new Thread(() => {
FireAndForget();
}) {
Name = "Running Work Thread (FireAndForget)",
Priority = ThreadPriority.BelowNormal
}).Start();
Related
I am trying to make a generic method that will cancel an Async web request and any additional operations associated with it. I found another question regarding this such as this.
And I have written a helper class that will do just this. I present it below:
public static class Helpers
{
public static async Task<string> GetJson(string url,
CancellationTokenSource cancellationTokenSource,
bool useSynchronizationContext = true)
{
try
{
var request = (HttpWebRequest)WebRequest.Create(url);
string jsonStringResult;
using (WebResponse response = await request.GetResponseAsync()
.WithCancellation(cancellationTokenSource.Token,
request.Abort, useSynchronizationContext))
{
Stream dataStream = response.GetResponseStream();
StreamReader reader = new StreamReader(dataStream);
jsonStringResult = await reader.ReadToEndAsync();
reader.Close();
dataStream.Close();
}
cancellationTokenSource.Token.ThrowIfCancellationRequested();
return jsonStringResult;
}
catch (Exception ex) when (ex is OperationCanceledException
|| ex is TaskCanceledException)
{
}
catch (Exception ex) when (ex is WebException
&& ((WebException)ex).Status == WebExceptionStatus.RequestCanceled)
{
}
catch (Exception ex)
{
//Any other exception
}
finally
{
cancellationTokenSource.Dispose();
}
return default;
}
public static async Task<T> WithCancellation<T>(this Task<T> task,
CancellationToken cancellationToken, Action action,
bool useSynchronizationContext)
{
using (cancellationToken.Register(action, useSynchronizationContext))
{
return await task;
}
}
}
Notice the line
cancellationTokenSource.Token.ThrowIfCancellationRequested();
right before returning the JSON string.
When the operation is canceled and the flow of execution is on line
StreamReader reader = new StreamReader(dataStream);
for example, all lines below (reader.Close() etc.) will be executed and the exception will be thrown when ThrowIfCancelationRequested() is executed - is that correct? Am I missing something?
If so, is there a way to cancel everything at once?
Thanks everyone for their response,
After the answer provided and all really useful comments I updated the implementation.I used HttpClient and the extension method in the link for having a task that cannot actually being canceled to behave like one that can - readAsStringAsync is actually executed since it cannot accept a cancellation token.
public static class Helpers
{
public static async Task<string> GetJson(string url,CancellationToken cancellationToken)
{
try
{
string jsonStringResult;
using (var client = new HttpClient())
{
cancellationToken.ThrowIfCancellationRequested();
using (var response = await client.GetAsync(url, cancellationToken))
{
jsonStringResult = await response.Content.ReadAsStringAsync().WithCancellation(cancellationToken);
}
}
return jsonStringResult;
}
catch (Exception ex) when (ex is OperationCanceledException)
{
}
catch (Exception ex) when (ex is WebException exception && exception.Status == WebExceptionStatus.RequestCanceled)
{
}
catch (Exception ex)
{
//LogException(ex);
}
return default;
}
public static Task<T> WithCancellation<T>(this Task<T> task, CancellationToken cancellationToken)
{
return task.IsCompleted
? task: task.ContinueWith(completedTask => completedTask.GetAwaiter().GetResult(),cancellationToken,TaskContinuationOptions.ExecuteSynchronously,TaskScheduler.Default);
}
}
all lines below (reader.Close() etc.) will be executed and the
exception will be thrown when ThrowIfCancelationRequested() is
executed - is that correct? Am I missing something?
If so, is there a way to cancel everything at once?
First of all, operation that you want to cancel has to explicitly support being canceled. So, you have to push your best to use a code for executing some operation in a way which somehow takes CancellationToken as an argument. If it is not possible, then you have no other chance.
That's why this concept is called Cooperative cancellation. Because almost always both sides should be aware of that cancellation happened. Client-side should be aware of that the code was actually canceled, it’s not enough for a client to know that cancellation was just requested. For the callee, it’s important to know about the fact that cancellation was requested in order to properly finish itself.
Regarding to checking whether operation is cancelled while executing Close method of stream and reader. You have to call cleanup methods always whether operation is cancelled or not if you want to avoid memory leaks. Of course I suggest you to use using statement which will automatically do that cleanup.
And by the way, for making some function cancelable you don't have to check whether cancellation is requested before executing each line. You just have to check whether cancellation is request before and after executing some long-running operation. And pass cancellation token if that long-running operations supports cancelling through cancellation token property.
Also, you have to take a look for side-effects. Don’t cancel if you’ve already incurred side-effects that your method isn’t prepared to revert on the way out that would leave you in an inconsistent state.
Some general code-block might be so:
if(ct.IsCancellationRequested)
{
break; // or throw
}
await DoSomething(ct);
if (ct.IsCancellationRequested)
{
// if there is no side-effect
return; // or throw
// or, we already did something in `DoSomething` method
// do some rollback
}
As a solution, you can use some different objects like HttpClient or WebRequest for executing async, awaitable and cancelable web request. You can take a look to that link for implementation details.
For few hours I am struggling with async code in C# and I can't really get why is my code deadlocked.
So far I've red many articles and anything ringed the bell for me.
Hope you can help me.
Here is the code I am trying to run.
Main
Task.Run(async () =>
{
Task<EventDetailed[]> events = getDetailedEvents();
await events;
}).Wait();
getDetailedEvents:
static async Task<EventDetailed[]> getDetailedEvents()
{
...
EventDetailed[] result = await LoadDetailedEventsDetailsAsync(evnts).ConfigureAwait(false);
return result;
}
And the core of my problem.
LoadDetailedEventsDetailsAsync
async static Task<EventDetailed[]> LoadDetailedEventsDetailsAsync(Event[] events)
{
List<EventDetailed> detailed = new List<EventDetailed>();
List<Task<WebResponse>> responses = new List<Task<WebResponse>>();
List<Event> tasksWithStream = new List<Event>();
foreach (Event e in events)
{
var httpWebRequest = (HttpWebRequest)WebRequest.Create("http://...");
... some headers etc ...
e.Stream = httpWebRequest.GetRequestStreamAsync();
e.WebRequest = httpWebRequest;
tasksWithStream.Add(e);
}
foreach (var tsk in tasksWithStream)
{
try {
await tsk.Stream.ConfigureAwait(false);
using (var streamWriter = new StreamWriter(tsk.Stream.Result))
{
streamWriter.Write("...");
streamWriter.Flush();
streamWriter.Close();
}
responses.Add(tsk.WebRequest.GetResponseAsync());
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
}
}
foreach (var response in responses)
{
try
{
await response.ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.mes("Failed to get event data.");
continue;
}
parseData.Add(ParseData(response));
}
A couple points:
First, it's important to note that you should almost never call .Wait (or .Result) on an async task - you should use await instead. One of the very few exceptions is in the Main method of a console app. The reason is that if you don't block the main thread, your program will simply exit prematurely.
Second, if you need to make multiple HTTP requests that do not depend on each other (i.e. request B does not need the results of request A), then there are huge performance gains to be had by executing them in parallel. Better still, you are not consuming a thread per request because the calls are asynchronous, i.e. they don't block a thread while waiting for a response, so the same thread can effectively fire off many concurrent requests.
I won't re-write your code, but I will suggest how I'd restructure it:
static void Main(string[] args)
{
// start all async tasks in parallel.
var tasks = GetEvents().Select(GetEventDetailsAsync);
// wait for them all to complete. normally you should use await instead of Wait,
// but you can't because you're in the main method of a console app.
Task.WhenAll(task).Wait();
}
static IEnumerable<Event> GetEvents()
{
// build a list of whatever metadata is needed to do your async work.
// do NOT do any actual async work here.
}
async static Task<EventDetailed> GetEventDetailsAsync(Event e)
{
// do all async work here, use await as needed,
// but only for one event (no loops).
}
I want to use the CancellationToken to abort a file download. This is what I tried:
public async Task retrieveDocument(Document document)
{
// do some preparation work first before retrieving the document (not shown here)
if (cancelToken == null)
{
cancelToken = new CancellationTokenSource();
try
{
Document documentResult = await webservice.GetDocumentAsync(document.Id, cancelToken.Token);
// do some other stuff (checks ...)
}
catch (OperationCanceledException)
{
Console.WriteLine("abort download");
}
finally
{
cancelToken = null;
}
}
else
{
cancelToken.Cancel();
cancelToken = null;
}
}
public async Task<Document> GetDocumentAsync(string documentId, CancellationToken cancelToken)
{
Document documentResult = new Document();
try
{
cancelToken.ThrowIfCancellationRequested();
documentResult = await Task.Run(() => manager.GetDocumentById(documentId));
}
return documentResult;
}
The cancelToken should then be used to cancel the operation:
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
if (cancelToken != null) {
Console.WriteLine ("Token cancelled");
cancelToken.Cancel ();
}
}
It seems that IsCancellationRequested is not updated. So the operation is not cancelled. I also tried to use this
cancelToken.ThrowIfCancellationRequested();
try{
documentResult = await Task.Run(() => manager.GetDocumentById (documentId), cancelToken);
} catch(TaskCanceledException){
Console.WriteLine("task canceled here");
}
but nothing changed.
What I'm doing wrong?
Edit:
Here are the missing parts like GetDocumentById:
public Document GetDocumentById(string docid)
{
GetDocumentByIdResult res;
try
{
res = ws.CallGetDocumentById(session, docid);
}
catch (WebException e)
{
throw new NoResponseFromServerException(e.Message);
}
return res;
}
public Document CallGetDocumentById(Session session, string parmsstring)
{
XmlDocument soapEnvelope = Factory.GetGetDocumentById(parmsstring);
HttpWebRequest webRequest = CreateWebRequest(session);
webRequest = InsertEnvelope(soapEnvelope, webRequest);
string result = WsGetResponseString(webRequest);
return ParseDocument(result);
}
static string WsGetResponseString(WebRequest webreq)
{
string soapResult = "";
IAsyncResult asyncResult = webreq.BeginGetResponse(null, null);
if (asyncResult.AsyncWaitHandle.WaitOne(50000))
{
using (WebResponse webResponse = webreq.EndGetResponse(asyncResult))
{
if (webResponse != null)
{
using (var rd = new StreamReader(webResponse.GetResponseStream()))
{
soapResult = rd.ReadToEnd();
}
}
}
}
else
{
webreq.Abort();
throw new NoResponseFromServerException();
}
return soapResult;
}
I want to use the CancellationToken to abort a file download
Downloading a file is an I/O operation, for which asynchronous cancelable (I/O completion port based) functions are available on the .NET platform. Yet you seem to not be using them.
Instead you appear to be creating (a chain of) tasks using Task.Run that perform blocking I/O, where a cancelation token is not passed on to each task in your Task.Run chain.
For examples of doing async, awaitable and cancelable file downloads, refer to:
Using HttpClient: How to copy HttpContent async and cancelable?
Windows Phone:
Downloading and saving a file Async in Windows Phone 8
Using WebClient: Has its own cancellation mechanism: the CancelAsync method, you can connect it to your cancellation token, using the token's Register method:
myToken.Register(myWebclient.CancelAsync);
Using the abstract WebRequest: If it was not created using an attached cancelation token, as seems to be the case for your edited example, and you are not actually downloading a file, but reading a content string, you need to use a combination of a few of the earlier mentioned methods.
You can do the following:
static async Task<string> WsGetResponseString(WebRequest webreq, CancellationToken cancelToken)`
{
cancelToken.Register(webreq.Abort);
using (var response = await webReq.GetResponseAsync())
using (var stream = response.GetResponseStream())
using (var destStream = new MemoryStream())
{
await stream.CopyToAsync(destStream, 4096, cancelToken);
return Encoding.UTF8.GetString(destStream.ToArray());
}
}
Your code only calls ThrowIfCancellationRequested() once after starting the GetDocumentAsync method, making the window for catching a cancel very small.
You need to pass the CancellationToken to GetDocumentById and have it either call ThrowIfCancellationRequested in between operations or perhaps pass the token straight to some calls at a lower level.
As a quick test of the plumbing between your calling method and the CancellationToken, you could change GetDocumentAsync to read:
cancelToken.ThrowIfCancellationRequested();
documentResult = await Task.Run(() => manager.GetDocumentById(documentId));
cancelToken.ThrowIfCancellationRequested();
And call CancelToken.CancelAfter(50) or simlar just after creating the CancellationTokenSource... You may need to adjust the value of 50 depending on how long GetDocumentById takes to run.
[Edit]
Given your edit to the question, the quickest fix is to pass the CancelToken down to WsGetResponseString and use CancelToken.Register() to call WebRequest.Abort().
You could also use CancelAfter() to implement your 50s timeout, switch from BeginGetResponse..EndGetResponse to GetResponseAsync etc.
I have the following extension methods for using asynchronous calls on WebRequest objects.
public static Task<WebResponse> GetReponseAsync(this WebRequest request)
{
return Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
}
public static Task<Stream> GetRequestStreamAsync(this WebRequest request)
{
return Task.Factory.FromAsync<Stream>(request.BeginGetRequestStream, request.EndGetRequestStream, null);
}
I would like to convert the following code to an asynchronous equivalent using these extension methods.
using (Stream rs = request.GetRequestStream())
{
var postData = Encoding.ASCII.GetBytes(PostData);
rs.Write(postData, 0, postData.Length);
using (WebResponse response = request.GetResponse())
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
str = reader.ReadToEnd();
rs.Close();
reader.Close();
response.Close();
}
}
I did this easily using another piece of code making usage of WebRequest, however that did not require the first call to GetRequestStream().
request.GetReponseAsync().ContinueWith(t =>
{
if (t.Exception == null)
{
using (var sr = new StreamReader(t.Result.GetResponseStream()))
{
str = sr.ReadToEnd();
}
}
});
How do I convert the first code block to use my extension methods and be equivalent?
EDIT I am using .NET 4.0, so async/await is not currently an option.
You just need to chain the ContinueWith calls. As a rule of thumb, you will have one ContinueWith per async operation in the sequence. Each ContinueWith will generally end with return <some async call> and the next will start process its result.
request.GetRequestStreamAsync()
.ContinueWith((trs) =>
{
var postData = System.Text.Encoding.ASCII.GetBytes("dummy");
trs.Result.Write(postData, 0, postData.Length);
return request.GetResponseAsync();
}).Unwrap()
.ContinueWith((resp) =>
{
using (var sr = new StreamReader(resp.Result.GetResponseStream()))
{
var str = sr.ReadToEnd();
}
});
Note that in my code (and your asynchronous version), not all of the objects are getting disposed as they were in the original.
At each step, you probably want to either check the Status or IsFaulted/IsCanceled properties or use the overload of ContinueWith that takes a TaskContinuationOptions parameter. For the latter option, beware that the previous task not completing in a way that matches the options results in a cancelled task. If you need to pass the errors through, that approach will not. Personally, I wrap all the checking into a method that either passes through errors and cancellation or runs a delegate on successful completion. Otherwise, you get a lot of boilerplate checking code very quickly.
I'm using HttpWebRequest to call a web service. If the AsyncCallback from BeginGetResponse throws an error, I want to propagate it up to my main program flow. I'm having trouble doing this because the error doesn't get propagated beyond the AsyncCallback. I've tried putting try/catch blocks at each step of the HttpWebRequest chain, but it never propagates beyond the "ResponseCallBack" method. Is it possible to get it back to the main thread?
private void StartRequest()
{
// Code to create request object is here
// ...
httpRequest.BeginGetRequestStream(new AsyncCallback(GetRequestStreamCallback), httpRequest);
}
private void GetRequestStreamCallback(IAsyncResult result)
{
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
// End the operation
var postStream = request.EndGetRequestStream(result);
string body = GenerateRequestBody();
// Convert the string into a byte array
byte[] postBytes = Encoding.UTF8.GetBytes(body);
// Write to request stream
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// Start the asynchronous operation to get the resonse
try
{
request.BeginGetResponse(new AsyncCallback(ResponseCallback), request);
}
catch (Exception)
{
throw;
}
}
private void ResponseCallback(IAsyncResult result)
{
string contents = String.Empty;
HttpWebRequest request = (HttpWebRequest)result.AsyncState;
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result);
using (Stream stream = response.GetResponseStream())
using (StreamReader reader = new StreamReader(stream))
{
contents = reader.ReadToEnd();
}
// Check the status
if (response.StatusCode == HttpStatusCode.OK)
{
//EXCEPTION NEVER MAKES IT PASSED HERE, EVEN IF I HAVE IT IN A TRY/CATCH BLOCK AND RE-THROW IT.
_object = ProcessResponseEntity(contents);
}
}
I think you're getting confused about the way asynchronous code exectution works and how the callback execution fits in with the calling code.
Within GetRequestStreamCallback, after the call to request.BeginGetResponse the method will continue to execute and in your example just end.
It is not known when (or even if) ResponseCallback will execute or what will be happening on the UI thread when it does. Because of this, ResponseCallback will execute on a different thread.
It's possible to have code within the callback run on the UI thread (which you'll need to do you want to interact with the UI) by using Dispatcher.BeginInvoke. However you can't have this execute within the context of another method.
While I wouldn't recommend it, you may want to have a look at this discussion on making the callback appear to execute synchronously. This will block your UI thread though and so is NOT recommended.