📜 ⬆️ ⬇️

Introduction to asynchronous operations in C # 5

Last time, we talked about how to simplify working with asynchronous operations using the Jeffrey Richter AsyncEnumerator class , as well as using the Reactive Extensions library. However, as Hejlsberg told us recently with the company, happiness will come with the release of a new version of the C # language and we will not need to use third-party and not-so-bicycles when working with asynchronous operations.

The idea that underlies the asynchronous operations in C # 5 is very similar to the one used by Jeffrey Richter in his AsyncEnumerator class, but this time the compiler also learned about it besides you and old Richter. this syntactic sugar). To begin with, let's go back to the synchronous version of our brilliant example that we used earlier:


static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  1. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  2. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  3. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  4. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  5. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  6. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  7. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  8. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  9. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  10. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  11. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  12. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  13. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  14. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  15. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  16. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  17. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  18. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
  19. static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .
static void SyncVersion() { Stopwatch sw = Stopwatch.StartNew(); string url1 = "http://rsdn.ru" ; string url2 = "http://gotdotnet.ru" ; string url3 = "http://blogs.msdn.com" ; var webRequest1 = WebRequest.Create(url1); var webResponse1 = webRequest1.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url1, webResponse1.ContentLength, sw.ElapsedMilliseconds); var webRequest2 = WebRequest.Create(url2); var webResponse2 = webRequest2.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url2, webResponse2.ContentLength, sw.ElapsedMilliseconds); var webRequest3 = WebRequest.Create(url3); var webResponse3 = webRequest3.GetResponse(); Console .WriteLine( "{0} : {1}, elapsed {2}ms" , url3, webResponse3.ContentLength, sw.ElapsedMilliseconds); } * This source code was highlighted with Source Code Highlighter .

')
Everything is very simple here (and the main thing is very original!): We pretend that we are writing something like our browser, well, or we just have nothing to do and need to get the contents of three web pages. The synchronous version works fine as always, except for the fact that we wait three times longer than we could by running this operation asynchronously. Since we already understood that this is bad and we watched Healesberg’s performance with pleasure, we’ll try to make this asynchronous method of this remarkable synchronous method with the help of two mouse strokes and three clicks on the keyboard.

The first thing to do is change the declaration of our function to the following:

  1. static async void AsyncVersion ()
* This source code was highlighted with Source Code Highlighter .


The async keyword (which is a keyword in the CTP version, and in the release will be a contextual keyword) in the method signature indicates that this method runs asynchronously and returns control to the calling code immediately after the start of some asynchronous operation. Asynchronous methods can return one of three types of return values: void , Task, and Task <T> . If the method returns void , it will be an asynchronous operation of the type: “started and forgotten” (“fire and forget”), since it will be impossible to handle the result of this operation (although if you do not handle exceptions inside this method, it will crash nicely, and it can pull along the whole process!). In some scenarios, this is useful (for example, we want to notify all remote subscribers asynchronously and we don’t care if they receive these messages or not). With the Task and Task <T> classes, the inquisitive reader (who is still not completely tired of the pace with which all of us favorite Redmond company releases new features) may be familiar, as they have been living and well for some time in the .Net Framework 4th version. The main idea of ​​these classes is that they encapsulate an “incomplete task” in themselves and we can wait for its completion, establish a “continuation” (something that should be caused when this task is completed), etc. At the same time, the Task <T> class is a successor of the Task class and differs from the latter in that it allows you to get a return value of type T through the Result property, while the Task class says that some task returns void , and is valuable due to its side effects.

Since we do not just want to start an asynchronous operation for execution, but also to get the results of its execution, but at the same time, it’s not the result itself that is important, but a side effect ... In general, we use the Task type as the return value (although we could safely use Task < string> , but, in general, it is not so important).

By changing the method signature, you need to change a little and its body. For this line of the form:

  1. var webResponse1 = webRequest1.GetResponse ();
* This source code was highlighted with Source Code Highlighter .


It should be replaced by:

  1. var webResponse1 = await webRequest1.GetResponseAsync ();
* This source code was highlighted with Source Code Highlighter .


The await keyword (in the release it will also be a context keyword) does not mean at all that we will stick at this point in the code until the requested asynchronous operation is performed. This can be confusing (and Eric and the company even suggest finding other keywords), but this means exactly the opposite idea. The await keyword means that after the start of an asynchronous operation (in this case, after the start of an asynchronous operation to retrieve WebResponse ), the method should return control, and continue from the same place after the end of the asynchronous operation. With wise words, this is called a continuation, and Eric has about a dozen articles that can coolly put brains on this issue.

If you don’t go into matane, then in this case you can draw a parallel with the blocks of iterators in C # (if you are not familiar with this topic, then I highly recommend you fix it ), because the compiler in both cases generates a finite state machine that tracks where it was method execution was interrupted, and is used to correctly restore execution on a subsequent call. However, if, in the case of iterators, resuming the execution of an iterator block occurs upon a subsequent call of the MoveNext method with an external code, the asynchronous method will continue to be executed automatically after the completion of the asynchronous operation. For this, the current method is set as the “continuation” of the current task, which is called when the current task is completed.

So, here is the entire modified method:

  1. static async Task AsyncVersion ()
  2. {
  3. Stopwatch sw = Stopwatch.StartNew ();
  4. string url1 = "http: //rsdn.ru1" ;
  5. string url2 = "http://gotdotnet.ru" ;
  6. string url3 = "http://blogs.msdn.com" ;
  7. var webRequest1 = WebRequest.Create (url1);
  8. Console .WriteLine ( "Before webRequest1.GetResponseAsync (). Thread Id: {0}" ,
  9. Thread.CurrentThread.ManagedThreadId);
  10. var webResponse1 = await webRequest1.GetResponseAsync ();
  11. Console .WriteLine ( "{0}: {1}, elapsed {2} ms. Thread Id: {3}" , url1,
  12. webResponse1.ContentLength, sw.ElapsedMilliseconds,
  13. Thread.CurrentThread.ManagedThreadId);
  14. var webRequest2 = WebRequest.Create (url2);
  15. Console .WriteLine ( "Before webRequest2.GetResponseAsync (). Thread Id: {0}" ,
  16. Thread.CurrentThread.ManagedThreadId);
  17. var webResponse2 = await webRequest2.GetResponseAsync ();
  18. Console .WriteLine ( "{0}: {1}, elapsed {2} ms. Thread Id: {3}" , url2,
  19. webResponse2.ContentLength, sw.ElapsedMilliseconds,
  20. Thread.CurrentThread.ManagedThreadId);
  21. var webRequest3 = WebRequest.Create (url3);
  22. Console .WriteLine ( "Before webRequest3.GetResponseAsync (). Thread Id: {0}" ,
  23. Thread.CurrentThread.ManagedThreadId);
  24. var webResponse3 = await webRequest3.GetResponseAsync ();
  25. Console .WriteLine ( "{0}: {1}, elapsed {2} ms. Thread Id: {3}" , url3,
  26. webResponse3.ContentLength, sw.ElapsedMilliseconds,
  27. Thread.CurrentThread.ManagedThreadId);
  28. }
* This source code was highlighted with Source Code Highlighter .


(To be honest, I still changed the output to the console so that you can see in which thread the method is currently running, but this is just for clarity, so this change does not count. But in any case, old Hejlsberg did not lie, change , indeed, the minimum amount).

And this is how this method is called:

  1. static void Main ( string [] args)
  2. {
  3. try
  4. {
  5. Console .WriteLine ( "Main thread id: {0}" , Thread.CurrentThread.ManagedThreadId);
  6. var task = AsyncVersion ();
  7. Console .WriteLine ( "Right after AsyncVersion () method call" );
  8. // Waiting for the completion of the asynchronous operation
  9. task.Wait ();
  10. Console .WriteLine ( "Asyncronous task finished!" );
  11. }
  12. catch (System.AggregateException e)
  13. {
  14. // All exceptions in TPL are thrown wrapped in AggregateException
  15. Console .WriteLine ( "AggregateException: {0}" , e.InnerException.Message);
  16. }
  17. Console .ReadLine ();
  18. }
* This source code was highlighted with Source Code Highlighter .


But the result of its implementation:

Main thread id: 10

Before webRequest1.GetResponseAsync (). Thread Id: 10


Right after AsyncVersion () method call


rsdn.ru : 1672, elapsed 657ms. Thread Id: 13

Before webRequest2.GetResponseAsync (). Thread Id: 13


gotdotnet.ru : 99470, elapsed 1915ms. Thread Id: 14

Before webRequest3.GetResponseAsync (). Thread Id: 14


blogs.msdn.com : 47927, elapsed 2628ms. Thread Id: 15

Asynchronous task finished!


And now let's look at in detail what is happening inside this beast. So, the call to the AsyncVersion method occurs in the current thread, and control returns immediately after the first await statement. But before returning control to the calling code, the AsyncVersion method is set as a “continuation” of the current task and the place from which to continue execution is remembered. This function returns an object of type Task , so that we can wait for completion and check the results. Then, after the completion of the asynchronous operation, execution resumes from the previous place and, as we see, already in a different thread. Further, this process continues until the third asynchronous operation is completed, after which the task.Wait method returns control, and we see the cherished on the console: “Asynchronous task finished!”.

Error handling has also undergone some changes, but also very minor ones. If you are already familiar with TPL, then you will recognize the familiar System.AggregateExcpetion class, which “collects” all the exceptions and accumulates them inside. The reason for this is that a single task can have a dozen child tasks, each of which may contain several more “subtasks”, and each task from this “tree” of tasks can fall with its own exception. It is for solving this task that AggregateException is used , which contains a “tree” of exceptions, which can be easily “straightened out” using the Flatten method (details can be found, for example, here , in the “Working with AggregateException” section). In general, if error handling has become complicated, it is not significant, and comparing it with the nightmare that we have to deal with when working with APM, then this “complication”, even the problem of calling fingers will not turn.

In fact, this example is not very different from the synchronous one; because we are performing three asynchronous operations in a row, with each subsequent operation only starting after the previous one is completed. Let's try to rewrite our second example in a new form, which simultaneously receives results from three web pages and asynchronously writes the result to a file:

  1. public static async void AsyncVersion2 ()
  2. {
  3. Stopwatch sw = Stopwatch.StartNew ();
  4. var urls = new string [] { "http://rsdn.ru" , "http://gotdotnet.ru" ,
  5. "http://blogs.msdn.com" };
  6. var tasks = ( from url in urls
  7. let webRequest = WebRequest.Create (url)
  8. select new {Url = url, Response = webRequest.GetResponseAsync ()})
  9. .ToList ();
  10. var data = await TaskEx.WhenAll (tasks.Select (t => t.Response));
  11. var sb = new StringBuilder ();
  12. foreach ( var s in tasks)
  13. {
  14. sb.AppendFormat ( "{0}: {1}, elapsed {2} ms. Thread Id: {3}" , s.Url,
  15. s.Response.Result.ContentLength,
  16. sw.ElapsedMilliseconds, Thread.CurrentThread.ManagedThreadId)
  17. .AppendLine ();
  18. }
  19. var outputText = sb.ToString ();
  20. Console .WriteLine ( "Web request results: {0}" , outputText);
  21. using ( var fs = new FileStream ( "d: \\ results.txt" , FileMode .Create,
  22. FileAccess.Write, FileShare .Write))
  23. {
  24. await
  25. fs.WriteAsync (UnicodeEncoding.Default.GetBytes (outputText), 0,
  26. UnicodeEncoding.Default.GetByteCount (outputText));
  27. }
  28. }
* This source code was highlighted with Source Code Highlighter .

This is really interesting! We received a completely asynchronous code, but at the same time it does not look as though a herd of Hindus worked on it for several nights (and the asynchronous code of similar complexity looks exactly like that). It is understandable and read in the same way as synchronous! (If someone does not believe, then let him try to rewrite this code in the classic APM style).

So what do we have? We have a piece that is really convenient to use without fear of shooting yourself a leg. In addition, even without going into serious jungle, it is more or less clear how to use this business (mda, although you will have to get used to the names). And the most interesting thing is that this is not all the functionality. I didn’t talk about synchronization contexts and how this business can be great used with the UI thread, what the TAP (Task-based Asynchronous Pattern) is, I didn’t say that all the new asynchronous methods I used are extension methods and you can add them yourself as much as you like, and did not go into the jungle of implementation ... But about it all, some other time! And today, I offer you, just download this thing and try it yourself!

(I almost forgot, here is the url so that you don’t search for it for a long time: msdn.com/vstudio/async)

Source: https://habr.com/ru/post/109345/


All Articles