This is a mirror of official site: http://jasper-net.blogspot.com/

Too Much, Too Fast with WPF and Async

| Sunday, February 17, 2013
.NET 4.5 added asynchronous language features for C# and VB. For the most part, this has made it much easier to improve a user interface’s responsiveness—you can use asynchronous APIs to perform potentially slow work in a way that will not cause your user interface to freeze, and yet you can use simple programming techniques that look very similar to those used in single-threaded code. However, this is not a panacea. There are some kinds of slow, IO-oriented work (i.e., the kind of work that often benefits most from asynchrony) where a simple application of these techniques won’t help as much as you might hope.

For example, you can run into trouble if you’re doing something that’s slow, but not quite slow enough. If you want to display a large amount of data from a file, a naive asynchronous (or, for that matter, multithreaded) approach can run into problems. The async and await keywords deal easily with long waits, but if you’re doing something slightly more busy you may need to apply these techniques with a bit more subtlety. This is the first in a series of blog posts exploring these issues.

The following code reads web server .log files. It reads all the files in a folder, and picks out the cs-uri-stem column, putting the result in an ObservableCollection<string>. We can bind that to a ListBox to show all of the URLs that have been fetched. (It’s a little crude—it ignores query strings for example, but for the log data I’m looking at, that happens not to matter, and the processing details aren’t the main point here.)

using System;
using System.IO;
using System.Collections.ObjectModel;

public class LogReader
{
    public LogReader()
    {
        LogUris = new ObservableCollection<string>();
    }

    public ObservableCollection<string> LogUris { get; set; }

    public void ReadLogs(string folder)
    {
        foreach (string logfile in Directory.GetFiles(folder, "*.log"))
        {
            using (StreamReader reader = File.OpenText(logfile))
            {
                int column = -1;
                while (!reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    if (line == null) { break; }

                    string[] fields = line.Split(' ');
                    if (line.StartsWith("#Fields"))
                    {
                        column = Array.IndexOf(fields, "cs-uri-stem") - 1;
                    }
                    else if (line[0] == '#' || fields.Length < (column + 1))
                    {
                        continue;
                    }
                    if (column >= 0)
                    {
                        string uri = fields[column];
                        LogUris.Add(uri);
                    }
                }
            }
        }
    }
}

I have a folder with about 40MB of log files, containing about 180,000 log entries. If I process this with my LogReader, and use it as the data source for a WPF ListBox, my aging desktop system takes about 2.6 seconds to load the information in that folder.

That’s clearly slow enough to be annoying, so you might think this would be an obvious candidate for going asynchronous. We’re loading a moderately large quantity of data from disk, so at least some of the work will be IO bound, and that’s where async usually shines.

Naive Asynchronous Implementation

At this point, an overenthusiastic developer might think “Ooh—async and await!” and make the obvious modifications. With .NET 4.5 and C#5 this is pretty simple. First, we need to change the signature of the method, adding the async keyword to enable the language features, and returning a Task to enable asynchronous completion, error reporting, and composition with other tasks:

public async Task ReadLogsAsync(string folder)

Read more: IanG on Tap
QR: Inline image 1

Posted via email from Jasper-net

0 comments: