Async in C #

    Continued links: part II and part III .

    At PDC2010, Heisberg announced that the next version of C # will support primitives for conveniently organizing asynchronous computing, in addition to the announcement, a CTP version of the extension for the studio ( 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 make asynchronous code look as close as possible to synchronous. Given that the Silverlight version of the .NET Framework contains only an asynchronous model for working with the network, this improvement is very appropriate.

    Despite the fact that simplified asynchronous programming is an innovation in C #, the approach itself 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 you to implement even more, so I was a little surprised when I looked at Heisberg's presentation and realized that only a special case was built into the language.

    So Async!


    Support for asynchronous programming in future C # is based on three innovations: types Task and Task, await operator, and async token.

    Task and Task

    These types describe asynchronous computation at runtime, as well as its result. You can draw an analogy with Thread. The following is part of the Task signature:

    public class Task : Task
    {
        public TResult Result { get; internal set; }
        public Task ContinueWith(
            Func, TNewResult> continuationFunction
        );
    }

    Imagine that we called a method that returned Task to us. Most likely, this means that the called method launched an asynchronous operation, the result of which will be string, and returned control to us, as well as an object that describes the asynchronous operation itself, without waiting for its completion. Having this object, we can turn to the Result field to get the result of the operation, in this case the current thread will pause until the asynchronous operation is completed.

    Another action we can perform with an object of type Task, this tells him that as soon as the asynchronous operation completes, he needs to run the continuationFunction with the result of the operation. Thus, combining several asynchronous operations, we get an asynchronous operation.

    In principle, you can use this approach to organizing asynchronous calculations 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 there is one logical method: get the result and process it, we must break it down to a method: one - start receiving the result, and the second - do the processing of the result. 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 a usage example:

    public async Task DownloadStringTaskSlowNetworkAsync(Uri address) {

    The token can be applied to the method that returns Task.or void. It is necessary 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 the AsyncCtpLibrary.dll library that comes with the extension already defines a large number of methods for working with basic asynchronous requests.

    The last key object that lightweight asynchronous operations are based on is the await statement. It is needed to combine a sequence of asynchronous operations into one. This operator takes an input object describing the asynchronous operation, and rewrites the code so that everything that follows the expression with await is converted into a closure and is an argument to the ContinueWith method of the object to which await is applied. We can say that this is an indication 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 is created that describes the asynchronous operation and it returns immediately.

    Consider an example that downloads and saves a web page asynchronously, it can be seen that the code looks synchronous, although the page loads 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 data)
        {
            try
            {
                stream.Write(data.Result);
            }
            finally
            {
                stream.Dispose();
            }
        });
    }

    Of course, the async method can contain several await calls, so you can combine asynchronous operations into one, for example, an 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 remains behind the scenes

    I did not write about synchronization tools for asynchronous operations (Task.WaitOne, Task.WaitAll), as well as multithreaded applications based on data flow (System.Threading.Tasks.Dataflow) and information about the progress of the operation. But it is worth noting that the release comes complete with a bunch of examples by which you can study the technology. In order to make them more interesting to study, there is a problem: in the example with DiningPhilosophers there is deadlock, you need to find the reason =)

    Also popular now: