明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 753|回复: 0

Finally working with the Async CTP for VS 2010

[复制链接]
发表于 2011-6-29 13:08:00 | 显示全部楼层 |阅读模式

作者:kean

原文地址:http://through-the-interface.typepad.com/through_the_interface/2011/06/finally-working-with-the-async-ctp-for-vs-2010.html

 

I’ve been planning to look at it for ages – and have certainly mentioned it before – but other things have kept on cropping up. Well last Thursday, on my train trip back from  Wallisellen (the home of Microsoft Switzerland), I finally managed to take the plunge and start working with the Async CTP for Visual Studio 2010. I’d been in Wallisellen to attend an MSDN TechTalk presentation by Stephen Toub, Principal Architect on Microsoft’s Parallel Computing Platform team. I’ve followed Stephen via his blog – and the Parallel Programming with .NET blog – for a long time, and thought it’d be a great opportunity to see him present in person (especially as such events in Switzerland tend to be quite intimate affairs – a great opportunity to have your questions answered).

I wasn’t disappointed: Stephen really (and I mean really) knows his stuff, and I left the seminar inspired and itching to try some things out with the Async CTP. I can only hope that developers working with Autodesk technology get the same feeling when leaving one of my presentations (it’s certainly a goal worth striving for, at least).

The ability to make asynchronous function calls easily is going to really significant for developers in .NET 5: I’ve been using  F#’s excellent Asynchronous Workflows capability to do that, before – getting significant speed-ups on sequential/synchronous code – but having the capability integrated directly into the .NET Framework makes it even simpler for such a use-case (where there’s an existing C# or VB.NET codebase). The basic premise is simple enough: when you call a synchronous method to get data over the wire (in particular, although you also get delays from accessing local storage) you have to wait for the results to come back. And if you’re getting multiple files and processing each one, you’re going to have significant overhead in your processing loop. The fact that synchronous calls block your UI is also significant: ideally you want to at least have the UI repaint – and probably show a progress bar – during such operations, rather than just going grey and showing the “Not responding” message in the application’s title bar.

Until now, asynchronous calls were typically handled by separate threads and lots of manual coordination code: you typically wrap your code up into a delegate and attach it to an event called on completion of asynchronous method (basically a very manual  continuation passing mechanism), which makes for messy, messy code.

The Async CTP (and presumably the same will be true of .NET 5) does all this under the covers: whenever you use the “await” keyword to wait for the results of an asynchronous call, execution continues in the outer, calling function – so it can continue to iterate through a loop, for instance – and the remaining code after the asynchronous call will get executed once the method returns. And that code will execute by default on the UI thread, which means you can update AutoCAD’s UI without having to implement any special coordination capability (in F# I used a MailboxProcessor for this).

Which means that you can literally take synchronous code and add a few keywords (a combination of async and await) – and change your method calls to the usually-also-available Async versions – and the code will speed up significantly. You not only stop your code from blocking while waiting for asynchronous calls to complete, you make more efficient use of your CPU(s) as the work gets spread across whatever cores are available. And all this with minimal changes to your application’s code. Too cool!

For now there are a few extra steps – such as adding a project reference to AsyncCtpLibrary.dll, which contains (no doubt among other things) asynchronous extension methods on System.Net.WebClient such as DownloadFileTaskAsync(), which creates an asynchronous task for which you can await completion – but these will disappear once the capabilities are integrated into the core .NET Framework. The steps for using the CTP are in this migration guide, which is well worth a look.

I went through the process for my BrowsePhotosynth application, which previous had three modes of operation: C# Synchronous, F# Synchronous and F# Asynchronous. It was really easy to add C# Asynchronous into the mix using the CTP.

Here’s the new source file, with the lines where it differs from the original synchronous version in red:

    1 using Autodesk.AutoCAD.DatabaseServices;

    2 using Autodesk.AutoCAD.EditorInput;

    3 using Autodesk.AutoCAD.Runtime;

    4 using System.Threading.Tasks;

    5 using System.Net;

    6 using System.IO;

    7 using System;

    8 

    9 namespace PhotosynthProcAsyncCs

   10 {

   11   public class PointCloudProcessor

   12   {

   13     Editor _ed;

   14     ProgressMeter _pm;

   15     string _localPath;

   16 

   17     public PointCloudProcessor(

   18       Editor ed, ProgressMeter pm, string localPath

   19     )

   20     {

   21       _ed = ed;

   22       _pm = pm;

   23       _localPath = localPath;

   24     }

   25 

   26     public async Task<long> ProcessPointCloud(

   27       string path, int[] dims, string txtPath

   28     )

   29     {

   30       // Counter for the total number of points

   31 

   32       long totalPoints = 0;

   33 

   34       // Create our intermediate text file in the temp folder

   35 

   36       FileInfo t = new FileInfo(txtPath);

   37       StreamWriter sw = t.CreateText();

   38       using (sw)

   39       {

   40         // We'll use a web client to download each .bin file

   41 

   42         WebClient wc = new WebClient();

   43         using (wc)

   44         {

   45           for (int maj=0; maj < dims.Length; maj++)

   46           {

   47             for (int min=0; min < dims[maj]; min++)

   48             {

   49               // Loop for each .bin file

   50 

   51               string root =

   52                 maj.ToString() + "_" + min.ToString() + ".bin";

   53               string src = path + root;

   54               string loc = _localPath + root;

   55 

   56               try

   57               {

   58                 await wc.DownloadFileTaskAsync(src, loc);

   59               }

   60               catch

   61               {

   62                 return 0;

   63               }

   64 

   65               if (File.Exists(loc))

   66               {

   67                 // Open our binary file for reading

   68 

   69                 BinaryReader br =

   70                   new BinaryReader(

   71                     File.Open(loc, FileMode.Open)

   72                   );

   73                 using (br)

   74                 {

   75                   try

   76                   {

   77                     // First information is the file version

   78                     // (for now we support version 1.0 only)

   79 

   80                     ushort majVer = ReadBigEndianShort(br);

   81                     ushort minVer = ReadBigEndianShort(br);

   82 

   83                     if (majVer != 1 || minVer != 0)

   84                     {

   85                       _ed.WriteMessage(

   86                         "\nCannot read a Photosynth point " +

   87                         "cloud of this version ({0}.{1}).",

   88                         majVer, minVer

   89                       );

   90                       return 0;

   91                     }

   92 

   93                     // Clear some header bytes we don't use

   94 

   95                     int n = ReadCompressedInt(br);

   96                     for (int i = 0; i < n; i++)

   97                     {

   98                       int m = ReadCompressedInt(br);

   99 

  100                       for (int j = 0; j < m; j++)

  101                       {

  102                         ReadCompressedInt(br);

  103                         ReadCompressedInt(br);

  104                       }

  105                     }

  106 

  107                     // Find the number of points in the file

  108 

  109                     int numPoints = ReadCompressedInt(br);

  110                     totalPoints += numPoints;

  111 

  112                     _ed.WriteMessage(

  113                      "\nProcessed points_{0} containing {1} " +

  114                      "points.",

  115                       root, numPoints

  116                     );

  117 

  118                     for (int k = 0; k < numPoints; k++)

  119                     {

  120                       // Read our coordinates

  121 

  122                       float x = ReadBigEndianFloat(br);

  123                       float y = ReadBigEndianFloat(br);

  124                       float z = ReadBigEndianFloat(br);

  125 

  126                       // Read and extract our RGB values

  127 

  128                       UInt16 rgb = ReadBigEndianShort(br);

  129 

  130                       int r = (rgb >> 11) * 255 / 31;

  131                       int g = ((rgb >> 5) & 63) * 255 / 63;

  132                       int b = (rgb & 31) * 255 / 31;

  133 

  134                       // Write the point with its color to file

  135 

  136                       sw.WriteLine(

  137                         "{0},{1},{2},{3},{4},{5}",

  138                         x, y, z, r, g, b

  139                       );

  140                     }

  141                   }

  142                   catch (System.Exception ex)

  143                   {

  144                     _ed.WriteMessage(

  145                      "\nError processing point cloud file " +

  146                      "\"points_{0}\": {1}",

  147                      root, ex.Message

  148                     );

  149                   }

  150                 }

  151 

  152                 // Delete our local .bin file

  153 

  154                 File.Delete(loc);

  155 

  156                 // Show some progress

  157 

  158                 _pm.MeterProgress();

  159                 System.Windows.Forms.Application.DoEvents();

  160 

  161                 if (

  162                   PhotosynthImporter.Commands.CheckEscape(_ed)

  163                 )

  164                 {

  165                   return 0;

  166                 }

  167               }

  168             }

  169           }

  170         }

  171       }

  172       return totalPoints;

  173     }

  174 

  175     private static int ReadCompressedInt(BinaryReader br)

  176     {

  177       int i = 0;

  178       byte b;

  179 

  180       do

  181       {

  182         b = br.ReadByte();

  183         i = (i << 7) | (b & 127);

  184       }

  185       while (b < 128);

  186 

  187       return i;

  188     }

  189 

  190     private static float ReadBigEndianFloat(BinaryReader br)

  191     {

  192       byte[] b = br.ReadBytes(4);

  193       return BitConverter.ToSingle(

  194         new byte[] { b[3], b[2], b[1], b[0] },

  195         0

  196       );

  197     }

  198 

  199     private static UInt16 ReadBigEndianShort(BinaryReader br)

  200     {

  201       byte b1 = br.ReadByte();

  202       byte b2 = br.ReadByte();

  203 

  204       return (ushort)(b2 | (b1 << 8));

  205     }

  206   }

  207 }

The calling code also ended up being very similar to the C# Synchronous implementation – the only difference being we receive a Task<long> back rather than a long (hopefully it’s also safe to just extract the Result from the Task – at least it worked well in my various tests):

PhotosynthProcAsyncCs.PointCloudProcessor pcp =

  new PhotosynthProcAsyncCs.PointCloudProcessor(

    ed, pm, localPath

  );

 

System.Threading.Tasks.Task<long> task =

  pcp.ProcessPointCloud(path, dims, txtPath);

totalPts = task.Result;

I know I’ll get asked whether I still prefer F# Asynchronous Workflows to the Async CTP capabilities. On the one hand, I certainly like the purity of describing tasks in F#’s more declarative style – this definitely appeals to me – but on the other hand (which must be the more pragmatic of the two :-) the fact you can integrate this capability with so few modified lines of C# code is astonishing. I’ll definitely keep space in my toolbox for both approaches.

In terms of performance, the C# Asynchronous implementation is equivalent to F# Asynchronous (and both are 8-10X quicker than their synchronous siblings, for this particular implementation scenario). I think I’m going to put together a quick screen-cam video to show this in action. I’ll hopefully post something in the next few days.

"觉得好,就打赏"
还没有人打赏,支持一下

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-12-24 07:10 , Processed in 0.181553 second(s), 31 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表