Rx, TPL, Async CTP–Oh My :-)

One of the comments on this blog last week when I posted around the release of the Reactive Extensions (Rx) was;

“Any chance you could provide a "brief" overview of where TPL, Rx and Async CTP all hang together??”

( that’s the Task Parallel Library, the Reactive Extensions and the Async CTP )

I thought I’d have a stab at it as, for me, writing things down tends to add a bit of clarity to how I’m thinking about them.

The problem I had though was making it brief as I find that there’s really a whole “soup” of technologies that fit into the space of concurrency and asynchronous working and it’s a bit hard to disentangle them.

The Shortest Version

  • TPL = ThreadPool++ Smile
  • AsyncCTP = TPL++ Smile
  • Rx = ‘Operators’ for dealing with streams of data values Smile

that’s it.

The Short Version

I ended up writing a short version…

  1. I see the Task Parallel Library as the modern API onto the .NET ThreadPool which lets us think about Tasks of work rather than Threads of execution. There’s huge advances in the level of abstraction over what went before and it hits both data and task parallelism.
  2. I see the Async CTP in some ways as syntactic “sugar” on top of the Task Parallel Library. That’s not meant to talk down the capabilities because I think it’s very strong syntactic sugar and I doubt that anyone would want to “go back” once that work is shipped. Maybe it’s syntactic chutney or something along those lines Smile
  3. I see the Reactive extensions as having a different focus. I see them as being an “algebra” or a “DSL” or a “language” for dealing with streams of data values where you want to combine, filter, project, delay, buffer, etc. those streams.

and so I do think that the TPL and Async CTP are very much linked together whereas I think that the Reactive work has a different focus although the technologies do overlap and combine.

Both the TPL and the Reactive Extensions are shipped technologies (TPL supported on .NET 4.0 and Rx supported in more places) whereas of course the Async CTP is work in progress.

I think these frameworks overlap in one area because the Reactive Extensions surface concurrency and can use the TPL to deliver that concurrency.

I also think they overlap because you can model anything as a stream of data values. You can model the number 42 as a stream of integers with only one element in the stream. Whether it makes sense to do so ultimately depends on what you’re going to do with that data and that would drive whether you took a Task based approach or a Reactive approach or perhaps a mix.

I also think they overlap because each offers a way of representing asynchronous work. For an async operation that returns T the TPL makes it easy for you to model that as a Task<T> and then use the TPL programming model to work with that operation. The async CTP makes this much more natural.

Similarly, for an async operation that returns T the ReactiveExtensions make it easy for you to turn that into a Func<IObservable<T>> and then use the Rx programming model to work with that operation as a stream of T.

So, there’s ways in which these technologies overlap and combine and I’d say that the Async CTP is a continuance of the TPL work whereas the Rx have that stream-based focus.

The Long Version

I also came up with a longer version…

First, the Earth cooled

In the beginning, the Earth cooled, the dinosaurs died and shortly afterwards .NET arrived and brought with it the end of the single-threaded program. There are places where the .NET CLR makes use of concurrent techniques to perform work such as garbage-collection and finalization and so you have to write your code to at least consider concurrency.

Concurrent Code

There are other places where you end up with concurrent code in .NET such as;

  1. You explicitly create a Thread using the Thread class.
  2. You make use of the thread pool by using the ThreadPoolclass to;
    1. Do a piece of work for you
    2. Wait for a synchronization object for you
    3. Bind a handle for you ( that’s much less common )
  3. You do some kind of asynchronous operation such as;
    1. You asynchronously invoke a delegate using BeginInvoke (pushing the invocation to the ThreadPool)
    2. You perform some asynchronous I/O using something like FileStream.BeginRead causing the OS to perform async IO and, through the ThreadPool, pick up the data from the I/O as it completes and deliver it back into your code on a ThreadPool thread.
  4. You are operating in an environment that already makes use of multi-threading such as ASP.NET, WCF, WF.

In terms of explicitly concurrent code, you essentially have the Thread and ThreadPool class and that’s pretty much it. There’s not really the notion of a higher level of abstraction unless you are writing in one of those server-side environments where your code is being hosted and you don’t so much have to worry about how it’s being hosted until it perhaps bites you one day Smile

There’s also not really the notion of being able to get “scalable concurrency” or “just enough concurrency” so if you have some data that could benefit from being processed in parallel on multiple cores then it’s very difficult to know how many items of work you should submit to the .NET ThreadPool in order to get that just right and keep all the cores busy and especially if your workload mixes IO with compute.

Asynchronous Code

In terms of asynchronous code, .NET started off with the Asynchronous Programming Model or APM which is based around the ideas of a class offering;

  1. an instance method named BeginMyOp which returns an IAsyncResult and takes whatever parameters are needed to start the async operation and (usually) a callback to be called when the operation completes.
  2. an instance method named EndMyOp which takes the IAsyncResult and returns whatever “future return value” you were hoping for when you began the async operation.

The APM is a bit of a pain when it comes to composability. If I’m writing some class that internally makes use of the FileStream class in order to do asynchronous reads then I have to surface my own BeginMyNewOp, EndMyNewOp methods on my new class and implement IAsyncResult somewhere myself and, while it’s not rocket science, it’s not much fun either.

Somewhere along the line, the recommended approach shifted from the APM towards simply making use of .NET events. That is – for an asynchronous operation you surface a method that begins it MyOpAsync and some event MyOpCompleted that fires when it is completed and delivers whatever data arose as a result of the asynchronous operation. This became known as the Event Asynchronous Pattern or EAP.

The EAP is definitely simpler than the APM but composability is still a bit limited in that if I’m writing some class that internally makes use of the WebClient class to do asynchronous HTTP GETs then I have to surface my own MyNewOpAsync method and fire a MyNewOpCompleted event and I have to sync up event handlers to the WebClient class and so on.

Co-ordination

Once you’ve got concurrent code, there’s a need to co-ordinate. Classes need to enforce thread-safety and .NET provided the basics in that area in the form of classes like Monitor and concurrently executing code needs to “rendezvous” from time to time with other code via classes like [Auto/Manual]ResetEvent and via bits and pieces like WaitHandle and so on.

Execution Context

When work moves from one thread to another there can be a need to transfer context about the work that’s being undertaken. Context might include information about security such as the principal, the call stack and so on.

If my memory is right then .NET 2.0 came along and made sure via the ExecutionContext class ( which I wrote about back in 2004 Confused smile ) that these things are correctly passed from one thread to another.

This little fragment of code (using the now redundant CAS) shows that;

  1. class Program   
  2. {   
  3.   const string fileName = @"c:\temp\foo.txt";   
  4.   
  5.   static void TryReadFile()   
  6.   {   
  7.     try   
  8.     {   
  9.       File.ReadAllText("c:\\temp\\foo.txt");   
  10.       Console.WriteLine("Read it!");   
  11.     }   
  12.     catch (SecurityException)   
  13.     {   
  14.       Console.WriteLine("Denied!");   
  15.     }   
  16.   }   
  17.   static void Main(string[] args)   
  18.   {   
  19.     FileIOPermission perm = new FileIOPermission(  
  20.       FileIOPermissionAccess.AllAccess, "c:\\temp\\foo.txt");   
  21.       
  22.     perm.Deny();   
  23.       
  24.     if (args.Length > 0)   
  25.     {   
  26.       ExecutionContext.SuppressFlow();   
  27.     }   
  28.     TryReadFile();   
  29.       
  30.     ThreadPool.QueueUserWorkItem(  
  31.       _ =>   
  32.       {   
  33.         TryReadFile();   
  34.       });   
  35.       
  36.     Console.ReadLine();   
  37.   }   
  38. }  

If that program is run with no command line arguments then the default behaviour of flowing the ExecutionContext from the original thread to the ThreadPool thread will occur which means that both attempts to read the file will fail.

However, if the program is run with any number of command line arguments then ExecutionContext.SuppressFlow() will be called and the restricted permission set of the stack will not flow across to the ThreadPool thread and so it will be able to read the file.

Synchronization Context

A part of the ExecutionContext that isn’t flowed when working with the ThreadPool or with creating Threads manually is the SynchronizationContext.

A SynchronizationContext provides a way of marshalling between threads. It’s pretty generic in that the crux of it looks like;

image

and so if you have a SynchronizationContext then you can Post it some work to do asynchronously or Send it some work to do synchronously and it’s up to the context to determine how/where to do that work.

  • implementations of SynchronizationContext in Windows Forms, WPF and Silverlight all use this in order to ensure that work which is posted or sent execute on the correct “UI” thread and don’t break the thread affinity of controls.
  • an implementation of SynchronizationContext in ASP.NET is used to ensure that if you’re working asynchronously then your threads have the right HttpContext and culture and so on while they’re executing code in asynchronous callbacks.

You can also use OperationStarted/OperationCompleted to signify the start/stop of a chunk of work such that a particular SynchronizationContext implementation can be aware of those boundaries

  • ASP.NET uses this such that when you are doing N asynchronous operations as part of rendering a page, it “knows” whether or not you’re still doing asynchronous work for that page or whether it’s over.

The place where I’ve generally come across SynchronizationContext is in the world of WPF/Silverlight/Windows Forms where you do work on a background thread and then need to communicate with a UI control which has thread affinity and so cannot be manipulated from that background thread and so you need the SynchronizationContext – i.e.

  1. void UIButtonClickHandler(object sender, EventArgs e)  
  2. {  
  3.   this.myUILabel.Text = "Calculating...";   
  4.   
  5.   SynchronizationContext uiCtx = SynchronizationContext.Current;   
  6.   
  7.   ThreadPool.QueueUserWorkItem(  
  8.     _ =>   
  9.     {   
  10.       SynchronizationContext threadPoolCtx = SynchronizationContext.Current;   
  11.       Debug.Assert(threadPoolCtx == null);   
  12.         
  13.       uiCtx.Post(o => this.myUILabel.Text = "Done!"null);   
  14.     });   
  15.   
  16. }  

note that the original thread had been set up ( by Windows Forms in this case ) to have a SynchronizationContext.Current set on it whereas the ThreadPool thread asserts that its context is set to NULL before using the one that it borrows from Windows Forms. The SynchronizationContext is not flowed automatically as part of the ExecutionContext.

From .NET 2.0 I think this stuff stayed relatively static until .NET 4.0…

then the TPL arrived ( the Task Parallel Library or perhaps the ThreadPooL 2.0 Winking smile)

The Task Parallel Library (TPL) was released as part of .NET 4.0 along with 2 other pieces of work in the concurrency arena;

  1. Improved data structures for coordination in .NET ( e.g. Barrier, ConcurrentQueue and more ).
  2. Parallel LINQ or PLINQ which builds upon the Task Parallel Library to add parallelised versions of the LINQ query operators like Select, Where and so on.

In many ways, I see TPL as a form of ThreadPool 2.0 – a modern UI for the ThreadPool which helps hugely in reducing the impedance between what I want to do;

  • a piece of work

and the building block of the API to get it done;

  • the thread

The Task Parallel Library came along and gave us Tasks to reason about and Tasks line up nicely with getting a piece of work done in that they allow for things like;

  • work items moving through a well defined life-cycle (Created, Running, etc.)
  • easy programmability for waiting for a Task or a group of Tasks to complete
  • a natural way of expressing parent/child relationships between tasks
  • cancellation to be a first class concept in the programming model
  • building up control flows where Task1 leads to Task2 under certain conditions (e.g. successful completion, cancellation, errors)

When it came to scheduling Tasks, the notion of an abstract TaskScheduler comes into play with different implementations but the most common being the .NET 4.0 ThreadPool via the ThreadPoolScheduler and the ThreadPool became aware of Tasks to add a bunch of optimisations such as;

  • giving each ThreadPool thread its own queue of Tasks
  • preferring to add any sub-tasks to the queue of the ThreadPool thread which was running the Task that created them
  • allowing a ThreadPool thread to steal work from the queue of another ThreadPool thread if there is nothing else to do

and you can take some control over this scheduling either via the options in the API ( e.g. TaskCreationOptions ) or via writing your own scheduler.

The other form of scheduler that you get with TPL is a SynchronizationContextScheduler which links the TPL back to the idea of SynchronizationContexts being used in order to do the work.

Some of the data-parallel aspects of Tasks (e.g. Parallel.For, Parallel.ForEach, Parallel.Invoke ) allow for “just enough parallelism” in that they cleverly create sub-tasks such that;

  1. if the ThreadPool heuristics determine that there is “CPU capacity” to inject another Thread then that Thread will work-steal the outstanding sub-tasks and execute them.
  2. if the ThreadPool heuristics determine that there is no “CPU capacity” to inject another Thread then the sub-tasks will get done by the existing Threads in due course.

and so this makes for a far better ThreadPool than .NET had prior to V4.0 with a much richer programming model and I’d recommend using it any day over working explicitly with Threads when you’re looking at compute-centric work.

It’s worth acknowledging that there are a number of scenarios in .NET where you’re not explicitly working with Threads but are, instead, hosted in an environment like ASP.NET or WCF that’s already dealing with threading for you and calling into your code in a particular context in order to get work done.

However, even there you could make use of the TPL in doing async processing and then marry that back up with (e.g.) ASP.NET via its SynchronizationContext.

Beyond the compute-centric work, the Task Parallel Library does “reach out” to deal with I/O work that in that the TaskFactory and TaskFactory<T> methods have FromAsync() methods which can be used in order to bridge between the newer world of the TPL and the older world of the Asynchronous Programming Model.

That means that you can write code such as this snippet to perform an asynchronous HTTP GET from microsoft.com;

  1. Task parentTask = new Task(  
  2.   () =>   
  3.   {   
  4.     WebRequest webRequest = WebRequest.Create("http://www.microsoft.com");   
  5.       
  6.     Task<WebResponse> task =   
  7.       Task<WebResponse>.Factory.FromAsync(  
  8.         webRequest.BeginGetResponse, webRequest.EndGetResponse,   
  9.         TaskCreationOptions.AttachedToParent);   
  10.       
  11.     task.ContinueWith(  
  12.       tr =>   
  13.       {   
  14.         using (Stream stream = tr.Result.GetResponseStream())   
  15.         {   
  16.           using (StreamReader reader = new StreamReader(stream))   
  17.           {   
  18.             string content = reader.ReadToEnd();   
  19.             Console.WriteLine(content);   
  20.             reader.Close();   
  21.           }   
  22.           stream.Close();   
  23.         }   
  24.       }, TaskContinuationOptions.AttachedToParent);   
  25.   });   
  26.   
  27. parentTask.RunSynchronously();   
  28. Console.WriteLine("Done");   
  29. Console.ReadLine();  

In that particular snippet, only the WebRequest.BeginGetResponse and WebRequest.EndGetResponse are being handed over to Task in order to make use of the FromAsync method that lets you turn this classic APM approach into a Task based approach.

However, it would probably make sense for me to also try and read the data asynchronously and write it asynchronously as well which I’m not doing in that snippet. That takes more effort;

  1. Task parentTask = new Task(  
  2.   () =>  
  3.   {  
  4.     WebRequest webRequest = WebRequest.Create("http://www.microsoft.com");  
  5.   
  6.     Task<WebResponse> task = Task<WebResponse>.Factory.FromAsync(  
  7.       webRequest.BeginGetResponse, webRequest.EndGetResponse,  
  8.       TaskCreationOptions.AttachedToParent);  
  9.   
  10.     task.ContinueWith(  
  11.       tr =>  
  12.       {  
  13.         Stream outStream = Console.OpenStandardOutput();  
  14.         Stream inStream = tr.Result.GetResponseStream();  
  15.   
  16.         byte[] buffer = new byte[24];  
  17.   
  18.         Action readWriteCycle = null;  
  19.         readWriteCycle = () =>  
  20.         {  
  21.           Task<int> readTask = Task<int>.Factory.FromAsync(  
  22.             inStream.BeginRead, inStream.EndRead,   
  23.             buffer, 0, buffer.Length,  
  24.             TaskCreationOptions.AttachedToParent);  
  25.   
  26.           readTask.ContinueWith(  
  27.             result =>  
  28.             {  
  29.               if (result.Result == 0)  
  30.               {  
  31.                 inStream.Close();  
  32.               }  
  33.               else  
  34.               {  
  35.                 Task writeTask = Task.Factory.FromAsync(  
  36.                   outStream.BeginWrite, outStream.EndWrite,   
  37.                   buffer, 0, result.Result,  
  38.                   TaskCreationOptions.AttachedToParent);  
  39.   
  40.                 writeTask.ContinueWith(  
  41.                   t =>  
  42.                   {  
  43.                     Task nextCycle = new Task(readWriteCycle,  
  44.                       TaskCreationOptions.AttachedToParent);  
  45.   
  46.                     nextCycle.Start();  
  47.                   }, TaskContinuationOptions.AttachedToParent);  
  48.               }  
  49.             }, TaskContinuationOptions.AttachedToParent);  
  50.         };  
  51.   
  52.         Task readWriteCycleTask = new Task(readWriteCycle,  
  53.           TaskCreationOptions.AttachedToParent);  
  54.   
  55.         readWriteCycleTask.Start();  
  56.       }, TaskContinuationOptions.AttachedToParent);  
  57.   });  
  58.   
  59. parentTask.RunSynchronously();   
  60.   
  61. Console.WriteLine("Done");   
  62. Console.ReadLine();  

and, if I managed to avoid screwing that up too much then that lets me use Task and be async around both the initial HTTP GET and then the reading of the network stream and the writing of the data to the console.

Between lines 18 to 59 of that snippet, there’s a form of “trick” that I have to use in order to set up a cycle that goes something like;

  1. Async Read
  2. Async Write
  3. Goto ( 1 ) if necessary

and tries to wrap all of that up in a set of Tasks and it’s worth saying that others have produced more elegant and innovative ways of achieving that kind of thing using techniques like async iterators (see post) but that stuff makes my head spin Smile

And so the way I see the TPL is that it offers a unified programming model around the Task that can move you away from having to code that the Thread/ThreadPool/APM level to a much higher and more productive level of working. I see it a little like;

image

i.e. I can code against Thread, ThreadPool and APM if I want to or I can code against Task and that’ll bring me a world of goodness where I can think in a more consistent way about what I want to get done rather than the mechanics of how to do it.

While the TPL is making it so much easier for me to program concurrently, there are still some stones in the road around programming asynchronously that even the  TPL can’t hide me from today as demonstrated on lines 18-59 of that previous example where I have to go to some lengths in order to set up the right sequence of asynchronous operations to get the job done.

For me, that’s where the async CTP comes in and builds on TPL to add…

then the Async CTP comes along…

I haven’t done so much with the Async CTP and C# 5.0 at this point because it feels like it might be some time away even with the arrival of the update for Sp1 which is explained in a C9 video here.

However, I did write a little about it in a post a while ago and linked to posts that others have written from there.

In the previous code snippet that I wrote, there were some fairly painful “seams” on display when I tried to marry up my use of Task with my use of the Asynchronous Programming Model from .NET. I had to go to quite a lot of trouble in order to build up a form of “control flow” with Task using the ContinueWith mechanism in order to get my reads/writes to not overlap each other and to complete in the correct order.

This happens around lines 18-59 of the previous code snippet and it was all pretty tedious and felt quite robotic.

With the work from the Async CTP, there’s no real need for me to write that kind of code any more because there are new language constructs coupled with code-generation capabilities to automate that kind of coding.

The way I see those new capabilities are;

  1. A way to write an “async” function that “returns” Task or Task<T> rather than an actual return value. A caller can then wait for the Task to complete asynchronously rather than expect an immediate “result”.
  2. A way to write a function that seems to have multiple return points ( “await” ) where the compiler restructures the code into a set of linked callbacks in a similar way to my lines 18-59 but far more comprehensively.
  3. A common abstraction for asynchronous work via the GetAwaiter() function and a bunch of extension methods that link existing classes into the new abstraction.

With that in mind, I tried to re-factor that code to make use of the Async CTP;

  1. Func<string, Task> task = async (url) =>   
  2. {   
  3.   WebRequest request = WebRequest.Create(url);   
  4.   Task<WebResponse> responseTask = request.GetResponseAsync();   
  5.   await responseTask;   
  6.     
  7.   Stream responseStream = responseTask.Result.GetResponseStream();   
  8.   Stream consoleStream = Console.OpenStandardOutput();   
  9.     
  10.   byte[] buffer = new byte[24];   
  11.     
  12.   Task<int> readTask = responseStream.ReadAsync(buffer, 0, buffer.Length);   
  13.   await readTask;   
  14.     
  15.   while (readTask.Result > 0)   
  16.   {   
  17.     await consoleStream.WriteAsync(buffer, 0, readTask.Result);   
  18.     readTask = responseStream.ReadAsync(buffer, 0, buffer.Length);   
  19.     await readTask;   
  20.   }   
  21.   responseStream.Close();   
  22. };   
  23.   
  24. task("http://www.microsoft.com").Wait();   
  25.   
  26. Console.WriteLine("Done");   
  27. Console.ReadLine();   

there’s a bunch of error handling missing there but I think it’s pretty clear that this is a lot more simple to follow than the previous code snippet.

It’s fairly clear that the flow is;

  1. Create web request.
  2. Asynchronously wait for the response.
  3. Get the response stream.
  4. Loop and asynchronously read from the response stream and write to the console stream (24 bytes at a time).

Essentially though, this is the same code that I wrote before except rather than me having to do a bunch of work myself to set up the asynchronous reads/writes the marriage of the;

  1. extension methods
  2. language constructs and code generation around them
  3. Task library

has brought things to a point where I can write the code pretty much as I would for the synchronous case with the compiler and the Task type doing the right things to re-structure the code as a set of linked callbacks.

So, for both compute-centric work and for async IO style work the TPL and the async CTP come together to get me to a very nice place.

The Async CTP also reaches out to the idea of SynchronizationContext – if I go back to my earlier example around handling a button click in Windows Forms then I can write something similar such as;

  1. async Task<string> LongRunningOperation()   
  2. {   
  3.   Task t = new Task(  
  4.     () =>   
  5.     {   
  6.       Thread.Sleep(2000);   
  7.     });   
  8.     
  9.   t.Start();   
  10.     
  11.   await t;   
  12.     
  13.   return ("Done");   
  14. }   
  15.   
  16. async void UIButtonClickHandler(object sender, EventArgs e)   
  17. {   
  18.   this.myUILabel.Text = "Calculating...";   
  19.   string result = await LongRunningOperation();   
  20.   this.myUILabel.Text = result;   
  21. }   

and so we have a UI event handler which goes off and performs some async operation and yet the code that deals with that is written in a transparent way without having to deal explicitly with callbacks and synchronization contexts.

Here’s hoping that the async work ships soon Smile

and so what about the Reactive Extensions (Rx) ?

With all of that goodness spread across the Task Parallel Library and the Async CTP, what room is there left for the Reactive Extensions (Rx) which shipped recently?

Quite a lot Smile

I think that the focus of Rx is quite different from that of the TPL and the Async CTP although there is commonality between the three.

For me, the focus of Rx is not about doing concurrent pieces of work or about performing asynchronous operations per se.

The focus is more around the idea of having a framework with rich capabilities for composing sequences of values.

Obvious examples might be;

  1. A sequence of stock prices.
  2. A sequence of news items.
  3. A sequence of mouse or keyboard events.

and the Rx framework has a set of “operators” which I almost see as a “DSL” for dealing with sequences of values that are produced over time. Those operators can do things like;

  • create or generate sequences
  • combine sequences
  • query, project, filter sequences
  • group, sort sequences
  • alter the temporal nature of data in sequences by delaying, buffering and so on

There’s overlap with the TPL because there’s a need for the work that some of the Rx operators do to be scheduled somewhere and one of the Rx options is to make use of the TPL (there are other options such as ThreadPool or “New Thread”) and so in that respect Rx sits on top of the TPL on .NET 4.0 although it doesn’t mean that Rx can’t work in other versions of .NET.

Rx also has ways of “reaching out” or “bridging” to other worlds such as the ability to easily convert a .NET event of EventHandler<T> into a sequence of values of type T. This makes it pretty easy to bridge between Rx and the EAP form of asynchronous work that’s present in .NET today.

It also reaches out again into the world of SynchronizationContexts by allowing for ObserveOn() and SubscribeOn() to be passed SynchronizationContext instances to be used in order to marry up nicely with those worlds.

Like the TPL, Rx also reaches out into the world of the traditional APM asynchronous world in .NET today in that it makes it relatively easy to model an asynchronous Begin/End operation as an IObservable<T>. Going back to that previous example;

  1. WebRequest request = WebRequest.Create("http://www.microsoft.com");   
  2.   
  3. Func<IObservable<WebResponse>> func =   
  4.   Observable.FromAsyncPattern<WebResponse>(  
  5.   request.BeginGetResponse, request.EndGetResponse);   
  6.   
  7. IObservable<Stream> streams =   
  8.   Observable  
  9.   .Defer(func)  
  10.   .Select(webResponse => webResponse.GetResponseStream());   
  11.   
  12. IDisposable subscription =   
  13.   streams.Subscribe(  
  14.   stream =>   
  15.   {   
  16.     using (stream)   
  17.     {   
  18.       using (StreamReader reader = new StreamReader(stream))   
  19.       {   
  20.         string content = reader.ReadToEnd();   
  21.         Console.WriteLine(content);   
  22.         reader.Close();   
  23.       }   
  24.       stream.Close();   
  25.     }   
  26.   });   
  27.   
  28. Console.ReadLine();   
  29.   
  30. subscription.Dispose();   

In a very similar way to what we did with the TPL, we use a pattern in the framework (FromAsyncPattern) in order to bridge from Rx to the APM.

In this example, I’m only doing the HTTP GET asynchronously and then reading the data synchronously. I’d also say that it feels a little “odd” to me to perform a single WebRequest and end up with a “stream” of WebResponse. I don’t really have a stream of WebResponse. I just have one and it feels a little unnatural to have to treat this as a stream.

If I wanted to do the reading of the data asynchronously as well then I’d have to take more steps and to match up with the previous examples I could perhaps try and return the data as a stream of byte[];

  1. string url = "http://www.microsoft.com";  
  2.   
  3. WebRequest webRequest = WebRequest.Create(url);  
  4.   
  5. Func<IObservable<WebResponse>> obsResponseFactory =  
  6.   Observable.FromAsyncPattern<WebResponse>(  
  7.     webRequest.BeginGetResponse, webRequest.EndGetResponse);  
  8.   
  9. IObservable<Stream> obsStreams =  
  10.   Observable  
  11.   .Defer(obsResponseFactory)  
  12.   .Select(wr => wr.GetResponseStream());  
  13.   
  14. IObservable<byte[]> obsBuffers =  
  15.   obsStreams  
  16.   .SelectMany(  
  17.     stream =>  
  18.     {  
  19.       Func<byte[], intint, IObservable<int>> obsReadFactory =  
  20.         Observable.FromAsyncPattern<byte[], intintint>(  
  21.           stream.BeginRead, stream.EndRead);  
  22.   
  23.       byte[] buffer = new byte[24];  
  24.   
  25.       return (  
  26.         Observable  
  27.           .Repeat(  
  28.             Observable  
  29.               .Defer(  
  30.                 () =>  
  31.                   obsReadFactory(buffer, 0, buffer.Length))  
  32.               .Select(byteCount =>  
  33.               {  
  34.                 byte[] newBuffer = new byte[byteCount];  
  35.                 Array.Copy(buffer, newBuffer, byteCount);  
  36.                 return (newBuffer);  
  37.               }),  
  38.             Scheduler.ThreadPool)  
  39.           .SelectMany(bits => bits)  
  40.           .TakeWhile(bits => bits.Length > 0));  
  41.     });  
  42.   
  43. obsBuffers.ForEach(  
  44.   bits => Console.Write(Encoding.Default.GetChars(bits)));  
  45.   
  46. Console.WriteLine("Completed");  
  47. Console.ReadLine();  

Now, in all honesty, I find that version of this example far more difficult to reason about and even to write than I did any of the other versions of the same example. I’m not at all sure that this is correct and it leaves me worrying about when and where some of that code is going to execute.

Update – I changed that last code snippet as Jerome noticed that I’d left a Repeat() operator running forever thereby pegging a CPU core Sad smile

However, I daresay that’s because I tried to stay entirely within the operators that Rx gives me and reading files 24 bytes at a time isn’t perhaps the key scenario for Rx but if I then wanted to do some more complex, stream based operations then it might make sense to start this way.

It’d be interesting to look at how I might try and sprinkle in some of the Async CTP work to see if I could make that code fragment easier to write and I’ll perhaps look at some of that in a follow on post as this one has got way too long and I’m starting to get bogged down in my own samples Smile

转载于:https://www.cnblogs.com/CoolTown/archive/2011/08/14/2138014.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值