Playing with Azure WebJobs SDK, when I trigger my function with CallAsync() on JobHost instance, the TextWriter and CancellationToken instances passed into my function get disposed in a second, which makes it useless for logging and monitoring of cancellation state.
For debugging I run my code as console app. The code within Main() looks like:
_host = new JobHost();
_host.StartAsync().Wait();
_host.CallAsync(typeof(Functions).GetMethod("ProcessAggregationsAsync"));
Console.ReadKey();
Here is the function (without logic):
[NoAutomaticTrigger]
public static async void ProcessAggregationsAsync([Table("table1")] CloudTable tbl, TextWriter log, CancellationToken cancelToken)
{
log.WriteLine("Hello");
...
log.WriteLine("Hello 2"); // --> log is disposed
}
In the function ProcessAggregationsAsync() there is an infinite loop which does some logic (monitoring azure tables and processing some data) and works fine. The problem starts when I add code to check cancellation token or log some characteristics into provided log (TextWriter) they are disposed. Not immediately, it writes 2 entries into log (azure table "azure-webjobs-hosts") but then it fails.
What could be wrong? Why those objects get disposed?
Oops, just noticed the obvious bug in the function signature - there should be Task instead of void (public static async Task ProcessAggregationsAsync(...)).
Doh. Why I hadn't seen it before I had to post this? :(
Related
I want to run a task like below :
Task.Run(() => GetWeatherAsync());
And this task only sleeps for 20 seconds :
public async void GetWeatherAsync()
{
System.Threading.Thread.Sleep(20000);
}
I want to prevent new user to enter to this scope (Method) until the previous Process is running .
What happens if the current user is waiting in GetWeatherAsync and new user enter .
Using Task.Run doesn't make your method async. Just use the GetWeatherAsync method without another overhead (because it's already async) and ASP.NET runs each request in a new thread. You don't need another thread here.
You should not use async void.
You should not use Thread.Sleep in async methods. It's Task.Delay here.
You can use locking here to achieve your goal:
// only 1 thread can be granted access at a time
static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1,1);
public async Task GetWeatherAsync()
{
// If no-one has been granted access to the Semaphore, code execution will proceed,
// otherwise this thread waits here until the semaphore is released
await semaphoreSlim.WaitAsync();
try
{
await Task.Delay(20000); // Your code here
}
finally
{
semaphoreSlim.Release();
}
}
P.S.
You need to start reading this Persian course about C# 5, Async.
I know that jaxws 2.2 specification says that all public non-static non-final method on a SEI which does not have WebMethod annotation with exclude as true, should be mapped to wsdl:operation elements.
It also says only the methods annotated with javax.jws.OneWay must be oneway operation, if the method does not have OneWay annotation (even if it has void return type and no exception) MUST NOT be mapped to oneway operations.
I don't find any reason to prevent void returning method with no exception mapping to oneway operation as default. This arises another question. why do we even need OneWay annotation when any method with void return type and no exception is oneway operation?
any method with void return type and no exception is oneway operation
This is not true. By default, void return type and no declared checked exception method is standard operation.
By default, client thread invoking a service will wait until receiving response from server (or client will timeout). Standard webservice operation, even with void return type, will respond with SOAP response (with empty body) after processing web method operation. It's synchronous invocation by default.
For example, if you have void type WebMethod with time consuming operation, service client will wait until all processing at server side is finished (assuming that no timeout occured), or will receive RuntimeException from server in case of fault. This will not happen with #OneWay operation.
In case of #OneWay methods, they are invoked asynchronously, so client thread will not wait for finishing server web method operations, and will proceed immediately.
Suppose I have an Action like below that I want to return the View asap and continue doing some work in the background thread.
public async Task<ActionResult> Index()
{
Debug.WriteLine("Inside Index");
var newCustomer = new Customer
{
Name = "Ibrahim"
};
Task.Run(() => SaveCustomer(newCustomer));
Debug.WriteLine("Exiting Index");
return View();
}
private async Task SaveCustomer(Customer NewCustomer)
{
Debug.WriteLine("Started Saving Customer");
await Task.Delay(2000);
Debug.WriteLine("Completed Saving Customer");
}
I do get the output as intended which is:
Inside Index
Exiting Index
Started Saving Customer
Completed Saving Customer
But what bothers me is that I get a warning that my Index action will run synchronously regardless and I should put an await but then the view is returned after SaveCustomer is completed and the purpose is defeated.
How am I doing this wrong? Any suggestion is appreciated.
But what bothers me is that I get a warning that my Index action will run synchronously
How am I doing this wrong?
Don't force asynchrony from the top down. Instead, start with naturally-asynchronous operations at the lowest level (e.g., EF6 database access), and allow the asynchrony grow from the lowest-level code upward.
Also, on ASP.NET, you should strongly avoid Task.Run.
Applying these two principles results in an Index method like this:
public async Task<ActionResult> Index()
{
Debug.WriteLine("Inside Index");
var newCustomer = new Customer
{
Name = "Ibrahim"
};
await SaveCustomer(newCustomer);
Debug.WriteLine("Exiting Index");
return View();
}
but then the view is returned after SaveCustomer is completed and the purpose is defeated.
Not at all. The purpose of asynchronous ASP.NET code is not to return early to the client. async and await do not change the HTTP protocol. await on the server side yields to the thread pool, not the client.
If you need to return early (and most people don't - they only think they "need" to), then you should use one of the established patterns for returning early (as I describe on my blog). Note that the only proper (i.e., fully reliable) solution entails setting up a reliable queue with an independent background process.
Your Index does not make use of any async feature at all. Why did you mark it async? You must be misunderstanding something, not sure what. Remove the async Task specification.
You get that compiler warning because there is nothing asynchronous in your Index() method. Your Task.Run(() => SaveCustomer(newCustomer)); line means Fire And Forget (non awaited task) - this is very different than asynchronous code. Index() is completely synchronous, while creating a "side Task" to execute sometime in the future. As the other answer mentioned - you could just as well remove the async mark from your method - it's not async.
On GET request I run (something like):
public ActionResult Index(void) {
webClient.DownloadStringComplete += onComplete;
webClient.DownloadStringAsync(...);
return null;
}
I see that onComplete isn't get invoked until after Index() has finished execution.
I can see that onComplete is invoked on a different thread from one Index was executed on.
Question: why is this happening? why is webClient's async thread is apparently blocked until request handling thread is finished?
Is there a way to fix this without starting new thread from ThreadPool (I tried this, and using thread pool does work as expected. Also webClient's callback does happen as expected if DownloadStringAsync is called from a ThreadPool's thread).
ASP.NET MVC 3.0, .NET 4.0, MS Cassini dev web server (VS 2010)
EDIT: Here is a full code:
public class HomeController : Controller {
private static ManualResetEvent done;
public ActionResult Index() {
return Content(DownloadString() ? "success" : "failure");
}
private static bool DownloadString() {
try {
done = new ManualResetEvent(false);
var wc = new WebClient();
wc.DownloadStringCompleted += (sender, args) => {
// this breakpoint is not hit until after Index() returns.
// It is weird though, because response isn't returned to the client (browser) until this callback finishes.
// Note: This thread is different from one Index() was running on.
done.Set();
};
var uri = new Uri(#"http://us.battle.net/wow/en/character/blackrock/hunt/simple");
wc.DownloadStringAsync(uri);
var timedout = !done.WaitOne(3000);
if (timedout) {
wc.CancelAsync();
// if this would be .WaitOne() instead then deadlock occurs.
var timedout2 = !done.WaitOne(3000);
Console.WriteLine(timedout2);
return !timedout2;
}
return true;
}
catch (Exception ex) {
Console.WriteLine(ex.Message);
}
return false;
}
}
I was curious about this so I asked on the Microsoft internal ASP.NET discussion alias, and got this response from Levi Broderick:
ASP.NET internally uses the
SynchronizationContext for
synchronization, and only one thread
at a time is ever allowed to have
control of that lock. In your
particular example, the thread running
HomeController::DownloadString holds
the lock, but it’s waiting for the
ManualResetEvent to be fired. The
ManualResetEvent won’t be fired until
the DownloadStringCompleted method
runs, but that method runs on a
different thread that can’t ever take
the synchronization lock because the
first thread still holds it. You’re
now deadlocked.
I’m surprised that this ever worked in
MVC 2, but if it did it was only by
happy accident. This was never
supported.
This is the point of using asynchronous processing. Your main thread starts the call, then goes on to do other useful things. When the call is complete, it picks a thread from the IO completion thread pool and calls your registered callback method on it (in this case your onComplete method). That way you don't need to have an expensive thread waiting around for a long-running web call to complete.
Anyway, the methods you're using follow the Event-based Asynchronous Pattern. You can read more about it here: http://msdn.microsoft.com/en-us/library/wewwczdw.aspx
(edit) Note: Disregard this answer as it does not help answer the clarified question. Leaving it up for the discussion that happened under it.
In addition to the chosen answer, see this article for further details on why the WebClient captures the SynchronizationContext.
http://msdn.microsoft.com/en-gb/magazine/gg598924.aspx
After complete of asynchronous call to WCF service I want set success message into session and show user the notification .
I tried use two ways for complete this operation.
1) Event Based Model.
client.GetDataCompleted += new EventHandler<GetDataCompletedEventArgs>(GetDataCompleted);
client.GetDataAsync(id, client);
private void GetDataCompleted(object obj, GetDataCompletedEventArgs e)
{
this.SetNotification(new Notification() { Message = e.Result, Type = NotificationType.Success });
}
In MyOperationCompleted event i can set notification to HttpContext.Current.Session, but I must waiting before this operation will completed and can't navigate to others pages.
2) IAsyncResult Model.
In this way I can navigate to other pages and make asynchronous calls to wcf service, but in GetDataCallback method can't set notification, becouse session = null.
client.BeginGetData(id, GetDataCallback, client);
private void GetDataCallback(IAsyncResult ar)
{
string name = ((ServiceReference1.Service1Client)ar.AsyncState).EndGetData(ar);
this.SetNotification(new Notification() { Message = name, Type = NotificationType.Success });
}
"Generate asynchronous operations" in service reference enabled.
Please help me with this trouble. Thanks.
I'm no wcf expert, but what I've found to work is wrapping your call to the Async version of your method in ThreadPool.QueueUserWorkItem. Without this, I had same blocking issue. So this seems to free up the main thread in your asp mvc to move on while another worker thread waits for the callback.
Also, I used AsyncController, although that alone was not enough without the worker thread.
See this: http://msdn.microsoft.com/en-us/library/ee728598.aspx
I used this as a guide, but still needed the ThreadPool.
Cheers