Continued on the links:
Part II and
Part III .
At PDC2010, Heisberg announced that the next version of C # will support primitives for convenient asynchronous computing, in addition to the announcement, the CTP version of the studio extension (
download ) was introduced, which allows you to try improved asynchronous programming now.
The improvement was created to facilitate the combination of asynchronous operations, as well as to ensure that the asynchronous code looked as close as possible to the synchronous one. Given that the Silverlight version of the .NET Framework only contains an asynchronous model for working with the network, this improvement is very appropriate.
')
Although simplified asynchronous programming is an innovation in C #, the approach cannot be called innovative, since the implementation of async based on monads is in Haskell, F # and Nemerle. In fact, the support of the monad language allows for even more, so I was a little surprised when I looked at the Heisberg presentation and realized that only a special case was built into the language.
So, Async!
Support for asynchronous programming in the future C # is based on three innovations: the Task and Task <T> types, the await operator, and the async token.
Task and Task <T>
These types describe asynchronous computing during execution, as well as its result. You can draw an analogy with Thread. Below is part of the Task <T> signature:
public class Task<TResult> : Task { public TResult Result { get; internal set; } public Task<TNewResult> ContinueWith<TNewResult>( Func<Task<TResult>, TNewResult> continuationFunction ); }
Imagine that we called the method that Task <string> returned to us. Most likely, this means that the called method started an asynchronous operation, the result of which will be a string, and returned to us the control, as well as the object that describes the asynchronous operation itself, without waiting for the completion of its execution. Having this object, we can refer to the Result field to get the result of the operation, in this case, the current thread pauses until the asynchronous operation is completed.
Another action we can take with an object of type Task <T> is to tell it that as soon as the asynchronous operation is completed, it needs to run a continuationFunction with the result of the operation. Thus, combining several asynchronous operations, we get an asynchronous operation.
In principle, this approach to the organization of asynchronous computing can be used now, since the described types belong to the System.Threading.Tasks namespace, which was introduced in the .NET Framework 4. But this use is not very convenient, since one logical method: get the result and process it, we have to break it down into a method: one is to start getting the result, and the second is to do the result processing. Therefore, the async token and the await operator were introduced.
Async and await
A token is very similar to an attribute or access modifier that applies to a method. Here is an example of use:
public async Task<string> DownloadStringTaskSlowNetworkAsync(Uri address) {
The marker can be applied to a method that returns Task <T> or void. You need to apply a marker to a method when a combination of other asynchronous calls occurs in the method body (using the await operator) or when the method defines an asynchronous operation, but this is rarely required, since a large number of methods are already defined in the AsyncCtpLibrary.dll library supplied with the extension. basic asynchronous requests.
The last key object on which lightweight asynchronous operations are based is the await operator. It is needed to combine a sequence of asynchronous operations into one. This operator accepts an object describing an asynchronous operation as input, and so rewrites the code so that everything that follows the expression with await is converted to a closure and is an argument of the object's ContinueWith method to which await is applied. We can say that this instruction to the compiler: "So, everything that comes after should be executed as soon as the current asynchronous operation is completed." The difference from accessing the Result property is that the current thread of execution is not suspended, but an object describing the asynchronous operation is created and it is immediately returned.
Consider an example that asynchronously downloads and saves a web page, it is clear that the code looks synchronous, although the page is loaded asynchronously:
class Program { static async Task SavePage(string file, string a) { using (var stream = File.AppendText(file)) { var html = await new WebClient().DownloadStringTaskAsync(a); stream.Write(html); } } static void Main(string[] args) { var task = SavePage("habrahabr", "http://habrahabr.ru"); task.Wait(); } }
The async / await compiler rewrites something like this:
static Task SavePage(string file, string a) { var stream = File.AppendText(file); return new WebClient().DownloadStringTaskAsync(a).ContinueWith(delegate(Task<string> data) { try { stream.Write(data.Result); } finally { stream.Dispose(); } }); }
Of course, the async method can contain several await calls, thanks to which you can combine asynchronous operations into one, for example, the example with saving web pages can be rewritten using asynchronous writing to a file with one minor change:
static async Task SavePage(string file, string a) { using (var stream = File.AppendText(file)) { var html = await new WebClient().DownloadStringTaskAsync(a); await stream.WriteAsync(html); } }
What's left overs
I did not write about the synchronization tools for asynchronous operations (Task.WaitOne, Task.WaitAll), as well as about multithreaded applications based on data flow (System.Threading.Tasks.Dataflow) and information on the progress of the operation. But it is worth noting that the release comes bundled with a bunch of examples by which you can study the technology. In order to be more interesting to study them, there is a problem: in the example of DiningPhilosophers there is a deadlock, you need to find the reason =)