Specific exceptions are not shown when using async/await and Task.Run - c#

I have a webpage with a list of recipes. In my code, I loop through the page and download each recipe. Here is a pseudo code of what I am doing :
//This is the Recipe class
//The constructor accepts a link to the recipe
//This method scrapes the recipe
public Task ScrapeAsync(){
return Task.Run(async () => {
string WebPage;
WebRequest request = WebRequest.Create(link);
request.Method = "GET";
using (WebResponse response = await request.GetResponseAsync())
using (StreamReader reader = new StreamReader(response.GetResponseStream())) {
WebPage = await reader.ReadToEndAsync();
}
//Non - async code here which is used to scrape the webpage
}
}
I used Task.Run because there are both async and blocking code in the Method.
//This is the RecipePage class
//This method scrapes the page
public Task GetRecipeListAsync(){
return Task.Run(async () => {
//I get the page using WebRequest and WebResponse in the same way as above
//Non - async/blocking code here which scrapes the page and finds links to recipes. I do await Recipe.ScrapeAsync() somewhere here.
//And I add the recipe objects to a public list in this class
}
}
In the form, it loops through a list of pages and do await RecipePage.GetRecipeList() and other things.
Here's where the for loop is:
private async void go_Click(object sender, EventArgs e){
for (int i = (int)startingPageNUD.Value; i <= (int)finishingPageNUD.Value; ++i) {
RecipePage page = new RecipePage(page + i);
await page.GetRecipeListAsync();
//Some other code here
}
}
My problem is that whenever an exception happens in the ScrapeAsync method, Visual Studio 2013 points to Application.Run(new Form1())
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
and tells me that Reflection.TargetInvocationException has occured. It does not show the actual exception in my code. For example, if I get a NullReferenceException in the code, it does not show that.
Because of this, I am having to write both async and non-async code and use non-async code to debug. Is there any way to solve this?
I have another question too. Am I using async/await and Task in the correct way?

Am I using async/await and Task in the correct way?
Pretty close. I don't see a need for Task.Run in this scenario (it should only be used to move CPU-intensive operations off the UI thread, and HTML scraping is usually fast enough to stay on the UI without any adverse effects).
The other recommendation I'd make is to have async Task methods return values as much as possible, instead of modifying member variables as side effects. In your case, consider having ScrapeAsync return its own list instead of updating a shared list, and having the calling code do the list merging.
Regarding your exception, TargetInvocationException will have an InnerException with the details. Exceptions in async void event handlers are managed in practically the same say as exceptions in regular void event handlers: if one escapes the event handler, then it goes to the application main loop. If you don't want this, you'll have to catch it (for both synchronous and asynchronous handlers).

Okay. Thanks to your edits I now can answer.
Your problem is this function:
private async void go_Click(object sender, EventArgs e){
for (int i = (int)startingPageNUD.Value; i <= (int)finishingPageNUD.Value; ++i) {
RecipePage page = new RecipePage(page + i);
await page.GetRecipeListAsync();
//Some other code here
}
}
The issue is that the function returns to what ever calls it once it reaches the await. Now if page.GetRecipeListAsync(); throws a exception, this exception is thrown inside the continuation handler. This handler is executed in the task queue of your UI. A exception thrown there crashes the task loop of your UI and this has all sorts of funny effects including strange exceptions.
In a async void function you should always handle any incoming exceptions by wrapping all code inside into a try…catch. If you crash the application if a exception occurs there or not is yours to decide.
The general way the things work is that any exception thrown inside a Task and by extend in a async function are thrown again by the await. But the async void functions are not awaited anywhere, so that does not work.
Regarding the usage of the async. I don't think you need to wrap every function into a Task but you can do that. Usually you don't need to force it into the background like this.

Encapsulate the getRecipeList code inside a try/catch.
In the catch code, get exception message and stack trace and assign them to variable(s) of Recipe class that you will test/display in your main thread when action is completed.
private string TheException = "" ;
public Task GetRecipeListAsync()
{
TheException = "" ;
Task result = Task.Run(async () =>
{
try
{
//I get the page using WebRequest and WebResponse in the same way as above
...
}
catch (Exception Ex)
}
if (TheException!="") MessageBox.show(TheException) ;
// or Throw an exception with
return result ;
}
you may also test "TheExceptio" in the GoClick procedure.

Related

I cannot understand how exactly does await/async work

I started to look into Task, async/await concepts is c# and I'm having big problems understanding it, well at least i don't know how to implement it. I started rewriting an older test program i had written before, but now instead of threading i want to use these new concepts. Basically the layout is as it follows:
I have a simple class where i download the HTML content of a web page.
I process that in another class where i basically just parse the page to my model. Later on i want to display that to my UI.
The problem is that my program is not responsive, it blocks the UI while I'm processing the info.
I started learning this 2 days ago, i have read a lot of stuff online, including MSDN and some blogs but yet I'm unable to figure it out. Maybe someone can provide a look as well
HtmlDOwnloadCOde:
public async Task<string> GetMangaDescriptionPage(string detailUrl)
{
WebClient client = new WebClient();
Stream data = await client.OpenReadTaskAsync(detailUrl);
StreamReader reader = new StreamReader(data);
string s = reader.ReadToEnd();
data.Dispose();
reader.Dispose();
data.Close();
reader.Close();
return s;
}
My parse class code:
public async Task<MangaDetailsModel> ParseMangaDescriptionPage()
{
ParseOneManga pom = new ParseOneManga();
string t1 = await pom.GetMangaDescriptionPage(selectedManga.url);
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(t1);
var divs = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
x.Attributes["id"].Value.Contains("title")).ToArray();
mangaDetails.mangaName = divs[0].Element("h1").InnerText;
mangaDetails.description = divs[0].Descendants("p").Single().InnerText ?? "DSA";
var tds = divs[0].Descendants("td");
int info = 0;
var chapters = htmlDoc.DocumentNode.Descendants("div").Where(x => x.Attributes.Contains("id") &&
x.Attributes["id"].Value.Contains("chapters")).ToArray();
var chapterUi = chapters[0].Descendants("ul").Where(x => x.Attributes.Contains("class") &&
x.Attributes["class"].Value.Contains("chlist"));
foreach (var li in chapterUi)
{
var liChapter = li.Descendants("li");
foreach (var h3tag in liChapter)
{
var chapterH3 = h3tag.Descendants("a").ToArray();
SingleManagFox chapterData = new SingleManagFox();
chapterData.name = chapterH3[1].InnerHtml;
chapterData.url = chapterH3[1].GetAttributeValue("href", "0");
mangaDetails.chapters.Add(chapterData);
}
};
return mangaDetails;
}
UI code:
private async void mainBtn_Click(object sender, RoutedEventArgs e)
{
if (mangaList.SelectedItem != null)
{
test12((SingleManagFox)mangaList.SelectedItem);
}
}
private async void test12(SingleManagFox selectedManga)
{
selectedManga = (SingleManagFox)mangaList.SelectedItem;
MangaDetails mangaDetails = new MangaDetails(selectedManga);
MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
txtMangaArtist.Text = mdm.artisName;
txtMangaAuthor.Text = mdm.authorName;
chapterList.ItemsSource = mdm.chapters;
}
Sorry if its trivial but i cannot figure it out myself.
When going async you need to try to go async all the way and avoid mixing blocking calls with async calls.
You are using async void in the event handler with no await.
Try to avoid async void unless it is an event handler. test12 should be updated to return Task and awaited in the event handler mainBtn_Click.
private async void mainBtn_Click(object sender, RoutedEventArgs e) {
if (mangaList.SelectedItem != null) {
await test12((SingleManagFox)mangaList.SelectedItem);
}
}
private async Task test12(SingleManagFox selectedManga) {
selectedManga = (SingleManagFox)mangaList.SelectedItem;
MangaDetails mangaDetails = new MangaDetails(selectedManga);
MangaDetailsModel mdm = await mangaDetails.ParseMangaDescriptionPage();
txtMangaArtist.Text = mdm.artisName;
txtMangaAuthor.Text = mdm.authorName;
chapterList.ItemsSource = mdm.chapters;
}
Also consider updating the web call to use HttpClient if available.
class ParseOneManga {
public async Task<string> GetMangaDescriptionPageAsync(string detailUrl) {
using (var client = new HttpClient()) {
string s = await client.GetStringAsync(detailUrl);
return s;
}
}
}
Reference: - Async/Await - Best Practices in Asynchronous Programming
Quite often people think that async-await means that multiple threads are processing your code at the same time. This is not the case, unless you explicitly start a different thread.
A good metaphore that helped me a lot explaining async-await is the restauran metaphor used in this interview with Eric Lippert. Search somewhere in the middle for async-await.
Eric Lipperts compares async-await processing with a cook who has to wait for his water to boil. Instead of waiting, he looks around if he can do other things instead. When finished doing the other thing, he comes back to see if the water is boiling and starts processing the boiling water.
The same is with your process. There is only one thread busy (at a time). This thread keeps processing until he has to await for something. This something is usually a fairly long process that is processed without using your CPU core, like writing a file to disk, loading a web page, or querying information from an external database.
Your thread can only do one thing at a time. So while it is busy calculating something, if can't react on operator input and your UI freezes, until the calculations are done. Async await will only help if there are a lot of times your thread would be waiting for other processes to complete
If you call an async function, you are certain that somewhere in that function is an await. In fact, if you declare your function async, and your forget to await in it, your compiler will warn you.
When your call meets the await in the function, your thread goes up its call stack to see if it can do other things. If you are not awaiting, you can continue processing, until you have to await. The thread goes up its call stack again to see if one of the callers is not awaiting etc.
async Task ReadDataAsync()
{
// do some preparations
using (TextReader textReader = ...)
{
var myReadTask = textReader.ReadToEndAsync();
// while the textReader is waiting for the information to be available
// you can do other things
ProcessSomething();
// after a while you really need the results from the read data,
// so you await for it.
string text = await MyReadTask;
// after the await, the results from ReatToEnd are available
Process(text);
...
There are some rules to follow:
an async function should return Task instead of void and Task<TResult> instead of TResult
There is one exception: the async event handler returns void instead of Task.
Inside your async function you should await somehow. If you don't await, it is useless to declare your function async
The result of await Task is void, and the result of await Task<TResult> is TResult
If you call an async function, see if you can do some processing instead of waiting for the results of the call
Note that even if you call several async functions before awaiting for them, does not mean that several threads are running these functions synchronously. The statement after your first call to the async function is processed after the called function starts awaiting.
async Task DoSomethingAsync()
{
var task1 = ReadAsync(...);
// no await, so next statement processes as soon as ReadAsync starts awaiting
DoSomeThingElse();
var task2 = QueryAsync(...);
// again no await
// now I need results from bothtask1, or from task2:
await Task.WhenAll(new Task[] {task1, task2});
var result1 = Task1.Result;
var result2 = Task2.Result;
Process(result1, result2);
...
Usually all your async functionality is performed by the same context. In practice this means that you can program as if your program is single threaded. This makes the look of your program much easier.
Another article that helped me a lot understanding async-await is Async-Await best practices written by the ever so helpful Stephen Cleary

Async Await Equivalent

When I run this code everything works fine:
public async void InvokePlugin(MyObject xTask)
{
try
{
var hndlr = new TimeoutHandler(RunTask);
var asyncResult = hndlr.BeginInvoke(xTask, null, new object());
if (!asyncResult.AsyncWaitHandle.WaitOne(xTask.Timeout, false))
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
hndlr.EndInvoke(asyncResult);
}
catch (Exception ex)
{
//Handle Exceptions
}
}
private delegate void TimeoutHandler(MyObject xTask);
I want to update this code to use Async/Await. I tried doing it like this:
public async void InvokePlugin(MyObject xTask)
{
try
{
var runTask = Task.Run(() => { RunTask(xTask); });
if (await Task.WhenAny(runTask, Task.Delay(xTask.Timeout)) == runTask)
{
// Task completed within timeout.
// Consider that the task may have faulted or been canceled.
// We re-await the task so that any exceptions/cancellation is rethrown.
await runTask;
}
else
{
throw new TimeoutException("Plugin didn't complete processing in a timely manner.");
}
}
catch (Exception ex)
{
//Handle Exceptions
}
}
...but it's not working. Clearly I'm doing somethign wring. It does call the RunTask Method and executes the first 2 lines fine but then it just ends and I can't seem to catch the exception in either the TaskRun method or code above. All I see in the Output windows is "Program has exited with code 0 (0x0)."
If the experts out there can either point me to what I'm doing wrong or give me suggestions as to how I can catch the exception and handle it I would be very grateful.
Also if you feel I missed any important details please ask and I will update my question.
Usually I'd say if it works don't fix it but in this case I'm trying to rearchitect a bit to allow for some enhancements so here I am.
Change async void to async Task. See my article on async best practices for more information.
After you do this, consume it asynchronously:
await the task, allowing async to grow.
async and await will naturally grow upward through your code base, until they reach Main, which cannot be async.
In your Main method, call GetAwaiter().GetResult() on the "top" task.
Blocking on asynchronous code is generally not a good idea, but blocking on a single task in a Console app's Main method is an exception to that rule.

A case when ConfigureAwait(false) causes an error instead of deadlock

Suppose I have written a library which relies on async methods:
namespace MyLibrary1
{
public class ClassFromMyLibrary1
{
public async Task<string> MethodFromMyLibrary1(string key, Func<string, Task<string>> actionToProcessNewValue)
{
var remoteValue = await GetValueByKey(key).ConfigureAwait(false);
//do some transformations of the value
var newValue = string.Format("Remote-{0}", remoteValue);
var processedValue = await actionToProcessNewValue(newValue).ConfigureAwait(false);
return string.Format("Processed-{0}", processedValue);
}
private async Task<string> GetValueByKey(string key)
{
//simulate time-consuming operation
await Task.Delay(500).ConfigureAwait(false);
return string.Format("ValueFromRemoteLocationBy{0}", key);
}
}
}
I followed the recommendations of using ConfigureAwait(false) (like in this post) everywhere in my library. Then I use it in synchronous way from my test app and get a failure:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button1_OnClick(object sender, RoutedEventArgs e)
{
try
{
var c = new ClassFromMyLibrary1();
var v1 = c.MethodFromMyLibrary1("test1", ActionToProcessNewValue).Result;
Label2.Content = v1;
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError("{0}", ex);
throw;
}
}
private Task<string> ActionToProcessNewValue(string s)
{
Label1.Content = s;
return Task.FromResult(string.Format("test2{0}", s));
}
}
}
The failure is:
WpfApplication1.vshost.exe Error: 0 :
System.InvalidOperationException: The calling thread cannot access
this object because a different thread owns it. at
System.Windows.Threading.Dispatcher.VerifyAccess() at
System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object
value) at System.Windows.Controls.ContentControl.set_Content(Object
value) at WpfApplication1.MainWindow.ActionToProcessNewValue(String
s) in
C:\dev\tests\4\WpfApplication1\WpfApplication1\MainWindow.xaml.cs:line
56 at
MyLibrary1.ClassFromMyLibrary1.d__0.MoveNext()
in
C:\dev\tests\4\WpfApplication1\WpfApplication1\MainWindow.xaml.cs:line
77
--- End of stack trace from previous location where exception was thrown --- at
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task
task) at
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task
task) at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at WpfApplication1.MainWindow.d__1.MoveNext() in
C:\dev\tests\4\WpfApplication1\WpfApplication1\MainWindow.xaml.cs:line
39 Exception thrown: 'System.InvalidOperationException' in
WpfApplication1.exe
Obviously the error happens because the awaiters in my library discard current WPF context.
From the other hand, after removing the ConfigureAwait(false) everywhere in the library I obviously get a deadlock instead.
There is more detailed example of code which explains some constraints that I have to deal with.
So how can I address this issue? What is the best approach here? Do I still need to follow the best practice regarding ConfigureAwait?
PS, In real scenario I have many classes and methods therefore tons of such async calls in my library. It's nearly impossible to find out if some particular async call requires context or not (see comments to #Alisson response) to fix it. I don't care about performance though, at least at this point. I'm looking for some general approach to address this issue.
Normally a library will document if a callback will be guaranteed to be on the same thread that called it, if it is not documented the safest option will be to assume it does not. Your code example (and the 3rd party you are working with from what I can tell from your comments) fall under the category of "Not guaranteed". In that situation you just need to check if you need to do a Invoke from inside the callback method and do it, you can call Dispatcher.CheckAccess() and it will return false if you need to invoke before using the control.
private async Task<string> ActionToProcessNewValue(string s)
{
//I like to put the work in a delegate so you don't need to type
// the same code for both if checks
Action work = () => Label1.Content = s;
if(Label1.Dispatcher.CheckAccess())
{
work();
}
else
{
var operation = Label1.Dispatcher.InvokeAsync(work, DispatcherPriority.Send);
//We likely don't need .ConfigureAwait(false) because we just proved
// we are not on the UI thread in the if check.
await operation.Task.ConfigureAwait(false);
}
return string.Format("test2{0}", s);
}
Here is a alternate version with a syncronous callback instead of a async one.
private string ActionToProcessNewValue(string s)
{
Action work = () => Label1.Content = s;
if(Label1.Dispatcher.CheckAccess())
{
work();
}
else
{
Label1.Dispatcher.Invoke(work, DispatcherPriority.Send);
}
return string.Format("test2{0}", s);
}
Here is another version if you wanted to get the value from Label1.Content instead of assigning it, this also does not need to use async/await inside the callback.
private Task<string> ActionToProcessNewValue(string s)
{
Func<string> work = () => Label1.Content.ToString();
if(Label1.Dispatcher.CheckAccess())
{
return Task.FromResult(work());
}
else
{
return Label1.Dispatcher.InvokeAsync(work, DispatcherPriority.Send).Task;
}
}
IMPORTANT NOTE: all of these methods will cause your program to deadlock if you don't get rid of the .Result in the button click handler, the Dispatcher.Invoke or the Dispatcher.InvokeAsync in the callback will never start while it is waiting for .Result to return and .Result will never return while it is waiting for the callback to return. You must change the click handler to be async void and do a await instead of the .Result.
Actually, you're receiving a callback in your ClassFromMyLibrary1 and you can't assume what it'll do (like updating a Label). You don't need ConfigureAwait(false) in your class library, as the same link you provided gives us an explanation like this:
As asynchronous GUI applications grow larger, you might find many
small parts of async methods all using the GUI thread as their
context. This can cause sluggishness as responsiveness suffers from
"thousands of paper cuts".
To mitigate this, await the result of ConfigureAwait whenever you can.
By using ConfigureAwait, you enable a small amount of parallelism:
Some asynchronous code can run in parallel with the GUI thread instead
of constantly badgering it with bits of work to do.
Now take a read here:
You should not use ConfigureAwait when you have code after the await
in the method that needs the context. For GUI apps, this includes any
code that manipulates GUI elements, writes data-bound properties or
depends on a GUI-specific type such as Dispatcher/CoreDispatcher.
You're doing exactly the opposite. You're trying to update GUI in two points, one in your callback method, and another here:
var c = new ClassFromMyLibrary1();
var v1 = c.MethodFromMyLibrary1("test1", ActionToProcessNewValue).Result;
Label2.Content = v1; // updating GUI...
That's why removing ConfigureAwait(false) solves your problem. Also, you can make your button click handler async and await your ClassFromMyLibrary1 method call.
In my opinion, you should redesign your library API to not mix a callback-based API with a Task-based API. At least in your example code there's no compelling case to be made to do that and you've nailed one reason not do do that - it is hard to control the context in which your callback runs.
I'd change your library API to be like so:
namespace MyLibrary1
{
public class ClassFromMyLibrary1
{
public async Task<string> MethodFromMyLibrary1(string key)
{
var remoteValue = await GetValueByKey(key).ConfigureAwait(false);
return remoteValue;
}
public string TransformProcessedValue(string processedValue)
{
return string.Format("Processed-{0}", processedValue);
}
private async Task<string> GetValueByKey(string key)
{
//simulate time-consuming operation
await Task.Delay(500).ConfigureAwait(false);
return string.Format("ValueFromRemoteLocationBy{0}", key);
}
}
}
And call it like so:
private async void Button1_OnClick(object sender, RoutedEventArgs e)
{
try
{
var c = new ClassFromMyLibrary1();
var v1 = await c.MethodFromMyLibrary1("test1");
var v2 = await ActionToProcessNewValue(v1);
var v3 = c.TransformProcessedValue(v2);
Label2.Content = v3;
}
catch (Exception ex)
{
System.Diagnostics.Trace.TraceError("{0}", ex);
throw;
}
}
private Task<string> ActionToProcessNewValue(string s)
{
Label1.Content = s;
return Task.FromResult(string.Format("test2{0}", s));
}

C# Windows Universal app not detecting async method

So I'm trying to write a simple universal app to get the price of bitcoin from the web. I have an async method that I got from here to get the json from a url and put it into a string. Here is where I called the method:
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
CoinPriceBackend CP = new CoinPriceBackend();
string response = await GetFromAPI();
}
And this is the method:
async Task<string> GetFromAPI()
{
try
{
//Create HttpClient
HttpClient httpClient = new HttpClient();
//Define Http Headers
httpClient.DefaultRequestHeaders.Accept.TryParseAdd("application/json");
//Call
string ResponseString = await httpClient.GetStringAsync(
new Uri("https://api.bitcoinaverage.com/ticker/GBP/"));
//Replace current URL with your URL
return ResponseString;
}
catch (Exception ex)
{
return "ERROR: " + ex;
}
}
I get the error
'The 'await' operator can only be used within an async method.
Consider marking this method with the 'async' modifier and changing its return type to 'Task'.'
But the method is async... How can I fix this?
Thanks!
But the method is async
Take a closer look at the error message; it's not talking about GetFromAPI - it's talking about App.
However, as others have pointed out, constructors cannot be marked async.
I'm trying to write a simple universal app
Universal Windows apps - like all other modern platforms - cannot block the UI thread for I/O-based operations. The user experience is just too bad, and there are tests in most app stores to auto-reject apps that do this.
Put another way: App is called (presumably) on application startup. When the user launches your app, it has to start up quickly and show something ASAP. Waiting for a download to complete is simply not an option.
So, to really fix this, you need to just start the download (not waiting for it to complete) and initialize your application to a "loading" state - showing a spinner or "Loading..." message or whatever. Then, when the download completes, update your app to display what you need to.
I have a blog post on async constructors and an article series on async MVVM (if you're doing MVVM), but a really basic approach could look something like this:
public Task Initialization { get; }
public string Value { get; private set { /* code to raise PropertyChanged */ } }
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
CoinPriceBackend CP = new CoinPriceBackend();
Value = "Loading..."; // Initialize to loading state.
Initialization = InitializeAsync();
}
private async Task InitializeAsync()
{
try
{
string response = await GetFromAPI();
...
Value = response; // Update data-bound value.
}
catch (Exception ex)
{
... // Display to user or something...
}
}
When you use an await within a function you should define the function as async.
But for App() constructor you will be unable to do that. You can defined another function from which you can call your function which return string.
Like this
public App()
{
CallApi();
}
private async void CallApi()
{
response = await GetFromAPI();
}
C# does not allow constructors to be marked as async.
You have three main options:
1) refactor to call this method in async event handler;
2) spawn a new thread using Task.Run and run async code there. This could lead to issues of marshalling result back to UI thread and assigning value to some UI element;
3) make it synchronous (blocking) call. This probably is the easiest option.
You will have to make following changes.
string response = GetFromAPI().Result;
Note that this will probably cause deadlock as Task will try to reaume in the main thread, which is already locked by call to '.Result', therefore you need another change. Also, there is no point
Task<string> GetFromAPI()
{
....
return httpClient.GetStringAsync(new Uri("https://api.bitcoinaverage.com/ticker/GBP/")).ConfigureAwait(false);
...
}

Creating an async webservice method

I've tried to read up on async methods and am now trying to create my own async method. The method is a webservice call that returns a list of error logs. I'm not sure that I've understood correctly so I thought I'd share my code to see if I should do anything different.
All I want the code to do is return a list of errorlogs by calling a method GetAllErrorLogs(), that is a synchronized method. Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method. Here is the code.
[WebMethod]
public async Task<List<ErrorLog>> GetAllErrorLogs()
{
List<ErrorLog> errorLogs = new List<ErrorLog>();
await System.Threading.Tasks.Task.Run(() => {
errorLogs = ErrorLogRepository.GetAllErrorLogs();
});
if (errorLogs == null)
return new List<ErrorLog>();
return errorLogs;
}
Thanks!
I recently gave a talk at ThatConference on async on the server side, and I address this issue in the slides.
On the server side, you want to avoid the use of Task.Run and other constructs that queue work to the thread pool. As much as possible, keep thread pool threads available for handling requests.
So, ideally your repository would have an asynchronous method GetAllErrorLogsAsync, which would itself be asynchronous. If GetAllErrorLogs cannot be asynchronous, then you may as well just call it directly (removing the await Task.Run).
Since it can take a second to fetch all the error logs I want to have the opportunity to do other stuff once I called the GetAllErrorLogs() method.
If you have a GetAllErrorLogsAsync available, then this can easily be done using Task.WhenAll. However, if GetAllErrorLogs is synchronous, then you can only do this by doing parallel work in your request (e.g., multiple calls to Task.Run followed by Task.WhenAll).
Parallel code on the server must be approached with great trepidation. It is only acceptable in a very limited set of scenarios. The entire point of async on the server side is to use fewer threads per request, and when you start parallelizing, you're doing the opposite: multiple threads per request. This is only appropriate if you know your user base is very small; otherwise, you'll kill your server scalability.
I found this great codeproject detailed article about how to achieve that
http://www.codeproject.com/Articles/600926/Asynchronous-web-services-call-in-ASP-NET
**This is potentially wrong, read comments or spinoff question at HttpContext.Current after an await
If ErrorLogRepository.GetAllErrorLogs() is not thread-safe, it will cause weird bugs and potentially exception out. Make sure your code is ready for multi-threaded operation before switching to async methods, this is obviously very trivial advice but often overlooked. For example, if you reference HttpContext.Current in your methods, your code will die in the async method, and sometimes even AFTER the await. The reason is that the code within the async block will potentially be run on a separate thread, which will not have access to the same HttpContext.Current thread-static property, and await gets compiled into two methods. All code before an await gets run on one thread, and then calls the code after an await keyword as a continuation, but potentially on yet another thread. So sometimes your code will even work in an async block, only to choke unexpectedly after it gets "out" of the async back to what you think is a synchronous part of your code (but in reality everything after an await keyword is already not guaranteed to be the original thread).
Here is some production code...
using System.Web.Http;
using AysncTask = System.Threading.Tasks.Task;
public class myController : ApiControllerBase
{
[HttpPut]
[Route("api/cleardata/{id}/{requestId}/")]
public async AysncTask ClearData(Guid id, Guid requestId)
{
try
{
await AysncTask.Run(() => DoClearData(id, requestId));
}
catch (Exception ex)
{
throw new Exception("Exception in myController.ClearData", ex);
}
}
}
Handling Async exceptions is also VERY VERY important.. although this is for a windows console app, the same principles should apply.
source: https://blogs.msdn.microsoft.com/ptorr/2014/12/10/async-exceptions-in-c/
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncAndExceptions
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += (s, e) => Log("*** Crash! ***", "UnhandledException");
TaskScheduler.UnobservedTaskException += (s, e) => Log("*** Crash! ***", "UnobservedTaskException");
RunTests();
// Let async tasks complete...
Thread.Sleep(500);
GC.Collect(3, GCCollectionMode.Forced, true);
}
private static async Task RunTests()
{
try
{
// crash
// _1_VoidNoWait();
// crash
// _2_AsyncVoidAwait();
// OK
// _3_AsyncVoidAwaitWithTry();
// crash - no await
// _4_TaskNoWait();
// crash - no await
// _5_TaskAwait();
// OK
// await _4_TaskNoWait();
// OK
// await _5_TaskAwait();
}
catch (Exception ex) { Log("Exception handled OK"); }
// crash - no try
// await _4_TaskNoWait();
// crash - no try
// await _5_TaskAwait();
}
// Unsafe
static void _1_VoidNoWait()
{
ThrowAsync();
}
// Unsafe
static async void _2_AsyncVoidAwait()
{
await ThrowAsync();
}
// Safe
static async void _3_AsyncVoidAwaitWithTry()
{
try { await ThrowAsync(); }
catch (Exception ex) { Log("Exception handled OK"); }
}
// Safe only if caller uses await (or Result) inside a try
static Task _4_TaskNoWait()
{
return ThrowAsync();
}
// Safe only if caller uses await (or Result) inside a try
static async Task _5_TaskAwait()
{
await ThrowAsync();
}
// Helper that sets an exception asnychronously
static Task ThrowAsync()
{
TaskCompletionSource tcs = new TaskCompletionSource();
ThreadPool.QueueUserWorkItem(_ => tcs.SetException(new Exception("ThrowAsync")));
return tcs.Task;
}
internal static void Log(string message, [CallerMemberName] string caller = "")
{
Console.WriteLine("{0}: {1}", caller, message);
}
}
}

Categories

Resources