Quick Tips
* Each managed thread in .NET reserves around 1MB of virtual memory
* Only a method marked async can contain the await keywords
Using NUnit to Run Test Methods
I’m using NUnit as a convenience to step through my test code. As of v2.6.2, NUnit supports unit tests with the async keyword.
Beginning with NUnit 2.6.2, test methods targetting .Net 4.5 may be marked as async and NUnit will wait for the method to complete before recording the result and moving on to the next test.Async test methods may return void or Task if no value is returned, or Task<T> if a value of type T is returned.
To support the test code here, I’ve added a general logger class that extracts the calling method and thread id:
public static class Logger
{ public static void Log(string message, [CallerMemberName]string caller = null) { LogInternal(message, caller); } private static void LogInternal(string message, string caller) { Console.WriteLine("{0:HH:mm:ss.ff} ({1:00}) [{2}]: {3}", DateTime.Now, Thread.CurrentThread.ManagedThreadId, caller, message); } }The following test has Timeout attribute assigned so it will fail as the task is not returned with 1000 milliseconds):
[Test] [Timeout(1000)] public async Task AsyncLambdaTimeout() { Logger.Log("Start"); Func<Task> slowTask = async () => { Logger.Log("Inside.."); await Task.Run(() => { Logger.Log("Sleeping1..."); Task.Delay(1500).Wait(); Logger.Log("...Awake1"); }); }; Logger.Log("Before await"); await slowTask(); Logger.Log("End"); }Rest results:
18:03:31.21 (09) [AsyncLambdaTimeout]: Start
18:03:31.23 (09) [AsyncLambdaTimeout]: Before await
18:03:31.24 (09) [AsyncLambdaTimeout]: Inside..
18:03:31.25 (04) [AsyncLambdaTimeout]: Sleeping1...
Test exceeded Timeout value of 1000ms
Notice how Logger.Log("...Awake1") never gets called as we waited 1500 milliseconds prior.
Return Statement
The return value from async method can be void, Task or Task<T>.
void is rarely used unless it’s truly a fire and forget operation (it's best to return async Task as easier for caller to use await to wait and makes exception handling easier)
For void and Task the return statement is optional or must be just “return;”
Task<T> must return a type T (the compiler will wrap this in a Task<T> for you)
Where async Cannot be Used
1. Catch (although that’s now changed in C#6)
[Test] public async Task<string> InvalidInCatch() { var result = string.Empty; try { result = await SlowRunningMethodWithError(); } catch (Exception) { // Give compiler error: Cannot await in the body of a catch clause result = await SlowRunningMethod(); } return result; }
2. Lock
There’s no point in locking on an object as the calling thread will release the synclock but get called back on a different thread.
private readonly object _sync = new object(); [Test] public async Task<string> InvalidInLock() { var result = string.Empty; var sync = new object(); lock (_sync) { // Gives compiler error: // The 'await' operator cannot be used in the body of a lock statement result = await SlowRunningMethod(); } return result; }
3. Most parts of linq
4. Unsafe code
Nothing to Return From an Async Call?
If you’ve got an async call that does return anything, ie some sort of fire and forget operation, it’s best to use async Task
Exceptions
Running this await call, the exception will be caught:
[Test] public async Task<string> AwaitCatchException() { var result = ""; try { result = await SlowRunningMethodWithError(); } catch (Exception ex) { // Will catch the exception Logger.Log(ex.ToString()); } return result; }
private static Task<string> SlowRunningMethodWithError(){ Logger.Log("SLOWIE: START"); var delay = Task.Run(() => { Task.Delay(2000).Wait(); throw new ApplicationException("oops"); return DateTime.Now.ToString("HH:mm:ss.ff"); }); Logger.Log("SLOWIE: END"); return delay; }
The MoveNext method name – line 17 in my case does refer to this line:
result = await SlowRunningMethodWithError();
18:42:26.50 (05) [SlowRunningMethodWithError]: SLOWIE: START
18:42:26.52 (05) [SlowRunningMethodWithError]: SLOWIE: END
18:42:28.55 (04) [CatchExceptions]: System.ApplicationException: oops
at AsyncAwaitDemo.AABasics.ExceptionTests.<SlowRunningMethodWithError>b__4() in ExceptionTests.cs:line 34
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
--- 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 ExceptionTests.<CatchExceptions>d__0.MoveNext() in ExceptionTests.cs:line 17
Conversely, calling SlowRunningMethodWithError as a task, the exception will only be caught when you await the task:
[Test]
public async Task<string> NeedsAwaitToGetException() { var result = ""; // Note exception will fire when we call await - not in the next line // this behaviour is a change to .NET!!!! var task = SlowRunningMethodWithError(); try { // exception will be trigged here result = await task; } catch (ApplicationException ex) { // Will catch the exception Logger.Log(ex.ToString()); } return result; }
Output:
18:58:21.52 (05) [SlowRunningMethodWithError]: SLOWIE: END
18:58:23.55 (04) [NeedsAwaitToGetException]: System.ApplicationException: oops
at ExceptionTests.cs:line 59
at System.Threading.Tasks.Task`1.InnerInvoke()
at System.Threading.Tasks.Task.Execute()
--- 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 ExceptionTests.cs:line 40
Recommendations on When to Await
Is it Running Async?
Just because you’re using an async method doesn’t mean it’s going to get called in an async manner. Methods start synchronously until an async method calls await
Given this slow running method that returns a Task<string>
private static Task<string> SlowRunningMethod() { Logger.Log("SLOWIE: START"); var delay = Task.Run(() => { Task.Delay(2000).Wait(); Logger.Log("After delay"); return DateTime.Now.ToString("HH:mm:ss.ff"); }); Logger.Log("SLOWIE: END"); return delay; }Running these two tests:
[Test] public async Task<string> AwaitDirect() { Logger.Log("Start"); // Following line is ran async - will have diffrent thread id var retValue = await SlowRunningMethod(); Logger.Log(string.Format("End. RetValue={0}", retValue)); return retValue; }
[Test] public async Task<string> DefineAsTaskAndAwait() { Logger.Log("Start"); // The method will start on the current thread - so will block it // it will return a Task<string> in the current thread var slowTask = SlowRunningMethod(); Logger.Log("In the middle"); var retValue = await slowTask; Logger.Log(string.Format("End. RetValue={0}", retValue)); return retValue; }Give this result:
AwaitDirect
|
DefineAsTaskAndAwait
|
(05) [AwaitDirect]: Start
(05) [SlowRunningMethod]: SLOWIE: START (05) [SlowRunningMethod]: SLOWIE: END
(04) [SlowRunningMethod]: After delay
(04) [AwaitDirect]: End. RetValue=18:06:28.26 |
(05) [DefineAsTaskAndAwait]: Start
(05) [SlowRunningMethod]: SLOWIE: START
(05) [SlowRunningMethod]: SLOWIE: END
(05) [DefineAsTaskAndAwait]: In the middle
(04) [SlowRunningMethod]: After delay
(04) [DefineAsTaskAndAwait]: End. RetValue=18:08:43.89
|
State Machine
Prior to calling to awaitable code, the compiler will generate a state machine containing a copy of all local variables:
- Parameters to the method
- Variables in scope
- Other variables, eg loop counters
- this is the method if non-static
More to follow
TreasureBox is operated by a group of young, passionate, and ambitious people that are working diligently towards the same goal - make your every dollar count, as we believe you deserve something better.
ReplyDeleteMattress
entertainment unit
furniture nz
The neatest thing about deposit bonuses is how varied they are often. Most online on line casino bonuses are for slots, however many casinos additionally offer deposit bonuses may be} exclusive to table games and stay on line casino games. If the 카지노사이트 wagering requirements are honest, then on line casino bonuses are completely legit and often value taking.
ReplyDelete