Welcome  |  Sign In
 | 
Skip Navigation Links
  

MSDN Magazine

Click to Rate and Give Feedback
Related Articles
Speech Server 2007 lets you create sophisticated voice-response applications with Microsoft .NET Framework and Visual Studio tool integration. Here’s how.

By Michael Dunn (April 2008)
Performance problems can creep into your Web app as it scales up, and when they do, you need to find the causes and the best strategies to address them.

By Richard Campbell and Kent Alstad (April 2008)
Animating with Silverlight is easier than you think. Here we create a 3D app that folds a polyhedron using XAML, C#, and by emulating the DirectX math libraries.

By Declan Brennan (April 2008)
Howard Dierking talks to the inventor of C++, Bjarne Stroustrup, about language zealots, the evolution of programming, and what’s in the future of programming.

By Howard Dierking (April 2008)
More ...
Articles by this Author
Jeffrey Richter introduces his AsyncEnumerator class and explains how it harnesses some recent additions to the C# programming language that make working with the asynchronous programming model significantly easier.

By Jeffrey Richter (November 2007)
Jeff Richter uses the AsyncResult class to implement the CLR’s Asynchronous Programming Model to perform hardware device operations asynchronously.

By Jeffrey Richter (June 2007)


By Jeffrey Richter (March 2007)
SideShow Gadgets for Windows Vista are cool. Writing your own is even better. Find out how it's done.

By Jeffrey Richter (January 2007)


By Jeffrey Richter (November 2006)
If multiple threads concurrently execute code that writes to or modifies a resource, then obviously the resource must be protected with a thread synchronization lock to ensure that the resource doesn't get corrupted.

By Jeffrey Richter (June 2006)
In my last column, I showed the various thread synchronization mechanisms employed by the Microsoft® . NET Framework (see Concurrent Affairs: Performance-Conscious Thread Synchronization). I then examined the performance characteristics of all these mechanisms and determined that the Interlocked methods performed the best because the calling thread never has to transition to kernel mode.

By Jeffrey Richter (March 2006)
In my career, I have architected and implemented many thread synchronization techniques. This has provided me with a lot of experience that has shaped the way I now think about thread synchronization problems.

By Jeffrey Richter (October 2005)
More ...
Popular Articles
SQL Server 2005 gathers a lot of useful data that you can use to identify areas where database performance can be improved. Here's what you need to improve performance.

By Ian Stirk (January 2008)
With custom form regions in Outlook you can pull in data from designated data sources and truly customize your users' Outlook 2007 experience.

By Steve Fox (Launch 2008)
Introducing Web-centric features of Windows Communication Foundation in the .NET Framework 3.5, including the HTTP programming model and the new syndication API.

By Justin Smith (January 2008)
Maria Blees introduces WinUnit, a handy new unit testing tool for native C++ projects.

By Maria Blees (February 2008)
More ...
Read the Blog
The Microsoft Office platform allows you to maintain both standard and custom properties for documents. Document Information Panels, however, let you implement additional functionality such as metadata-based search and automation of information-driven business processes is Office documents and SharePoint apps. In the April 2008 issue of MSDN ...
Read more!
Going Places is a new MSDN Magazine column devoted to mobile device development. The main focus will be on Windows Mobile phones and Tablet PCs, although any topic related to mobility is fair game. In the April 2008 issue of MSDN ...
Read more!
Silverlight is a new cross-browser plug-in from Microsoft that brings the power of the .NET Framework to bear on an area that was previously reserved for Flash or Java Applets. Silverlight also supports a range of .NET-compliant languages, including Visual Basic and C#, so you don't have to learn a new language to build rich media applications. In the April 2008 issue ...
Read more!
Performance problems can creep into your ASP.NET app as it scales up, and when they do, you need to determine what the actual problem is and find the best strategies to address it. In the April 2008 issue of MSDN Magazine, Richard Campbell and Kent Alstad present three ...
Read more!
One of our editorial counterparts over in MSDN, Mitch Irsfeld, the Managing Editor of the U.S. MSDN Flash and TechNet Flash newsletters, visited Imagine 2008 and reported back on what the computer industry is doing to promote the principles of environmental sustainability. Check ...
Read more!
With the My Extensibility feature, new in Visual Basic 2008, My namespace extensions can be activated or deactivated through the Project Properties Designer, or when an associated reference is added or removed from a project. This capability makes extending the Visual Basic development environment simple through deploying APIs for common coding tasks. In the April 2008 issue ...
Read more!
More ...
Concurrent Affairs
Concurrency and Coordination Runtime
Jeffrey Richter

Code download available at: ConcurrentAffairs2006_09.exe (154 KB)
Browse the Code Online
Microsoft recently announced the prerelease of a new Microsoft Robotics Studio for writing applications for robots. By itself, it is interesting to see this new toolkit, but it should have appeal even beyond just those interested in programming robots. Under the hood, Microsoft is powering this SDK with some very advanced technologies, including a lightweight distributed services-oriented architecture and a common language runtime (CLR)-based library called the Concurrency and Coordination Runtime (CCR). The CCR makes programming asynchronous behavior much simpler than the typical challenge of writing threaded code. This is a very significant benefit for writing robot applications because they require handling many processes (sensors and motors) at the same time. You should note that this column is based on a prerelease version of both Microsoft® Robotics Studio and the CCR. All the information on these technologies is subject to change.
So why is this interesting beyond robotics? Today many applications lack responsiveness and scalability. It is common to see applications that periodically hang and stop responding to user input as well as server applications that do not respond to client requests in a timely fashion. Who among us hasn't seen a Web browser time out due to a server not responding quickly enough? The reason applications hang and have poor responsiveness is almost always due to performing I/O operations (such as file reads/writes, Web requests, and database queries) synchronously.
When an application's thread performs synchronous I/O requests, the application is basically giving up control of the thread's processing to the I/O device (a hard drive, a network, or whatever). The application's responsiveness then becomes unpredictable. Furthermore, when threads are suspended waiting for I/O requests to complete, the application tends to create more threads in an attempt to accomplish more work. However, creating, scheduling, and destroying a thread requires time and memory and can actually hurt performance rather than improve it.
There are two main reasons why developers tend to write code that performs synchronous I/O instead of asynchronous I/O. The first reason is because it's easier to write code that performs synchronous I/O. When performing asynchronous I/O, the developer has to separate the concepts of initiating the I/O request from the completion of the I/O request. The real problem here is not the divorcing of the concepts as much as the syntax required. The second reason is because it is hard to coordinate the actions you want performed when the I/O requests complete.
The CCR library is a managed DLL that greatly simplifies these tasks for the programmer. The CCR offers a number of classes allowing developers a simple object model that they can use to easily express complex coordination patterns for dealing with completed I/O operations. Furthermore, the CCR offers its own high-performance thread pool you can use to execute tasks in response to completed I/O. The thread pool offers phenomenal scalability and will maximize concurrency within your application. When you couple the CCR with some of the new C# language features (such as anonymous methods and iterators), you get a developer's dream come true: an easy way to write responsive and scalable applications.
I'll describe the CCR's architecture and object model, and show numerous examples that demonstrate how the CCR works as well as how to use it in your own applications. To compile my demo code and to play with the CCR, you must first download it. You'll find download information at the end of this column.
To use the CCR, there are just a few classes you'll need to become familiar with. These classes are defined in the Microsoft.Ccr.Core namespace. Figure 1 shows the relationship between these classes. Please refer to this figure as I describe the classes.
Figure 1 CCR Class Hierarchy 

The Dispatcher Class
When your application initializes, you'll first want to construct a Dispatcher object that creates and manages a set of threads. In effect, it is a thread pool. Like the CLR's thread pool, these threads call methods (via delegates) in order to execute tasks:
public sealed class Dispatcher : IDisposable {
    public Dispatcher();
    public Dispatcher(Int32 threadCount, String threadPoolName);
    public Dispatcher(Int32 threadCount, ThreadPriority priority,
        String threadPoolName);
    public ICollection<DispatcherQueue> DispatcherQueues { get; }
    ... // Other members not shown
}
When you construct a Dispatcher object, you can pass to the constructor the number of threads you desire. By default, the Dispatcher creates one thread for every CPU in your computer. Notice that the number of threads created by a Dispatcher object is fixed; there is no logic in the Dispatcher for dynamically creating or destroying threads. And unlike the CLR's thread pool, there is no special thread that runs periodically checking the workload trying to predict whether threads should be dynamically added or removed from the thread pool. This streamlines the Dispatcher's thread pool logic and contributes to its high performance.
When constructing a Dispatcher, you also get to set its threads' scheduling priority. By default, the threads are created with normal priority. You can also tell the Dispatcher what string name to give the threads that it creates. Internally, when the Dispatcher creates its threads, it sets their Name property to your specified string. These names are used to help debugging and can be seen when debugging you application using the Visual Studio® debugger's Threads window.
Unlike the CLR's one-and-only thread pool, the CCR allows you to create multiple thread pools by creating multiple Dispatcher objects. This allows you to create sets of threads (at differing priorities, if desired) dedicated to processing certain kinds of tasks.

The DispatcherQueue Class
After you've constructed a Dispatcher, you'll want to construct a DispatcherQueue object. A DispatcherQueue maintains a queue of delegates that identify methods ready to execute. The Dispatcher's threads wait for entries to appear in the DispatcherQueue. Usually, the DispatcherQueue's queue is empty and therefore the Dispatcher's threads are waiting. As entries appear, Dispatcher threads wake up and execute the methods:
public sealed class DispatcherQueue : IDisposable {
    // Use CLR thread pool; not Dispatcher
    public DispatcherQueue(string name); 
    public DispatcherQueue(String name, Dispatcher dispatcher);

    public virtual void Enqueue(ITask task);
    public virtual void EnqueueTimer(
        TimeSpan timeSpan, Port<DateTime> timerPort);
   
    public void Dispose();

    ... // Other members not shown
}
With the CLR's thread pool, if 1,000 items are queued up, there is no way for a new item to be processed until all of the first 1,000 items have been extracted from the thread pool's queue. But with the CCR you can use one DispatcherQueue object for most work items and use another DispatcherQueue object for high-priority work items. The Dispatcher's threads dequeue entries from all the DispatcherQueue objects associated with it in a round-robin fashion. I should also point out that calling the constructor that doesn't take a Dispatcher argument makes it possible to create a DispatcherQueue object that queues its tasks to the CLR's thread pool instead of using a Dispatcher's thread pool.
It is common for an application to construct its Dispatcher and DispatcherQueue objects during initialization and use them throughout the remainder of the application's lifetime. So, at this point, we'll turn our attention to the classes that an application tends to construct, use for a short time, and then discard.

The Port and Arbiter Classes
A generic Port<T> object represents a queue of items, all of type T. You can think of a Port as a way to queue an input argument to a callback method, which is analogous to the state argument that you pass to ThreadPool's QueueUserWorkItem method.
When asynchronous I/O operations complete, their result will be posted to a Port object. I'll explain how this happens later in this column. A Port object also has zero or more ReceiverTask objects associated with it. When an item gets posted, the ReceiverTask objects determine how to coordinate and process the item. In the Port<T> class definition that follows I do not show the methods that register and unregister ReceiverTask objects because you will not usually call these methods yourself. Instead, you will use the Arbiter's methods to create ReceiverTask objects to register these objects with a Port object:
public class Port<T> : /* interfaces not shown */ {
   public Port();
   public virtual void Post(T item);
   // Other members not shown
}

The Arbiter Class
The Arbiter class, shown in Figure 2, is what you use to tap into the CCR's coordination features; you will use this class a lot when programming against the CCR. Arbiter is a static class that defines a bunch of methods that are factories for creating other objects. Specifically, when you call one of Arbiter's static factory methods, the method constructs an arbiter that has fields that refer to one or more ReceiverTask objects that are also constructed.
Figure 3 shows the common arbiters offered by the CCR with a brief description indicating what each arbiter does. Note that the CCR defines even more arbiters for less-common scenarios. For example, there is a JoinReceiver class that calls a method when many Port objects (of varying data types) have items posted into them. Also note that you can define your own arbiters, but this is a very advanced feature which I won't describe here. If you are interested in understanding arbiters more, I encourage you to examine the types in the Microsoft.Ccr.Core and Microsoft.Ccr.Core.Arbiters namespaces.
You'll notice that some arbiters (Receive, MultipleItemReceive, MultiplePortReceive, and JoinedReceive) take a Boolean value indicating whether the arbiter is persistent. Also, some arbiters (such as Choice and Interleave's TeardownReceiverGroup) require that their arbiters always be non-persistent. Understanding an arbiter's persistence is very important to working effectively with the CCR and I will explain persistence as I walk through some code examples.
After you've composed all the arbiters that describe your desired coordination, you must activate the arbiters by calling Arbiter's Activate method. Calling the Active method is very important because it registers all of the arbiter's ReceiverTask objects with the Ports and it tells the arbiter which DispatcherQueue to post work item tasks to as Port items are processed. If you forget to activate your arbiters, items will continue to queue up in Port objects but they will never get processed!
As items are posted into a Port object, the registered ReceiverTask objects send the items to their arbiter object which decides what callback method should ultimately execute to process the posted item. The arbiter object then queues the work item task into a DispatcherQueue, and a Dispatcher (or CLR thread pool) thread will ultimately execute the method processing the posted item.

Code Examples
The best way to understand the CCR and how it works is to examine several code examples while I explain what is happening. To this end, I have created a program with a bunch of small methods in it. Each method demonstrates some part of the CCR. I will now walk through these methods, all of which can be found in the CCRDemos.cs file in the download for this column available from the MSDN®Magazine Web site.
Initialization and Shutdown The Main method of my example demonstrates a common way to initialize the CCR. It simply creates a Dispatcher and a DispatcherQueue and then it executes various methods that demonstrate uses of the CCR. The DispatcherQueue object is passed to each demo method so that all methods use the same work item queue and thread pool. Since the Dispatcher object is constructed in a using statement, it will have its Dispose method called just before Main returns, exiting the application. The Dispatcher's Dispose method tells all the thread pool threads to gracefully exit, and after they have all exited, the Dispose method returns to its caller (Main in Figure 4). The demos make use of some helper methods as well:
private static void Msg(String format, params Object[] args) {
    Console.Write("ThreadID={0}: ",        
    	Thread.CurrentThread.ManagedThreadId);
    Console.WriteLine(format, args);
}

private static void HitEnter() {
    Thread.Sleep(250);   // Give an operation a chance to complete
    Console.Write("Hit <Enter> to continue demo");
    Console.ReadLine();
    Console.WriteLine();
}
SpawnDemo The SpawnDemo method demonstrates how to spawn a task using the CCR's thread pool. In my example, I use the C# anonymous method feature to define a callback method that calls my Msg method. C# will automatically construct a delegate over this anonymous method and that delegate is passed to Arbiter's static FromHandler method which wraps the delegate into another object used natively by the CCR. Then Arbiter's static Activate method is called to queue the callback method to the specified DispatcherQueue object. This simple task spawning is similar to using ThreadPool.QueueUserWorkItem:
private static void SpawnDemo(DispatcherQueue dq) {
    Arbiter.Activate(dq, Arbiter.FromHandler(
        delegate { Msg("Handler"); }));
    HitEnter(); // wait for user input to continue
}
PortArbiterDemo The PortArbiterDemo method demonstrates how Port and Arbiter objects work together. In this method, I first construct a Port<String> object which internally contains a first-in-first-out (FIFO) queue capable of holding references to String objects. A Port object also maintains a list of registered ReceiverTask arbiter objects. Then, I call the Post method to have the Port process a String item ("StringA"):
private static void PortArbiterDemo(DispatcherQueue dq) {
    Port<String> stringPort = new Port<String>();
    stringPort.Post("StringA");
When an item is posted to a port, the Post method traverses the list of ReceiverTask objects to see if any of them want to handle the item. If no ReceiverTask objects are registered or if none of the registered ReceiverTask objects want the item, the item is added to the Port's internal queue. In my example, I have not yet registered any ReceiverTask objects and therefore "StringA" is placed in the Port's queue.
The next thing the PortArbiterDemo method does is call Arbiter's Receive method which creates a ReceiverTask object. The first argument, false, indicates that this ReceiverTask object should not be persistent. In other words, once this ReceiverTask object processes an item, it should not be used to process another item. The second argument, stringPort, tells the ReceiverTask object which Port object it should be watching for items. The third argument, an anonymous method delegate, tells the ReceiverTask object what method should be called to process the Port's item.
Arbiter.Activate(dq,
    Arbiter.Receive(false, stringPort, 
        delegate(String s) { Msg("Handling string={0}", s); }));
It is important to note that calling Arbiter's Receive method simply constructs a ReceiverTask object. This object is not registered or activated with a Port object yet. Furthermore, there needs to be a way to tell the ReceiverTask object which DispatcherQueue object to queue the delegate callback method to. The call to Arbiter's Activate method does both of these things.
When Arbiter's Activate method is called, it registers the ReceiverTask object with the Port. During the registration process, the Port's previously queued items are scanned. If the ReceiverTask object wants the item, it will process it by queuing the callback delegate into the DispatcherQueue so that a Dispatcher thread will execute the code that processes the item.
In my example, I posted an item to the Port and then registered a ReceiverTask object. However, it is possible to register ReceiverTask objects with a Port first and then post items to the Port. In this case, the items are processed as they are posted and are only placed in the queue if no ReceiverTask object wants to process it. This is very efficient.
PostArbiterDemo then posts "StringB" to the port:
stringPort.Post("StringB");
Since I created my ReceiverTask object by passing false for the persistent argument, it is allowed to process just one Port item and then the ReceiverTask object is automatically unregistered from the Port. When the PortArbiterDemo method posts "StringB", there are no registered ReceiverTask objects and "StringB" gets placed in the Port's queue; the string is not displayed in the console window.
Next, I create another ReceiverTask object but this time, I pass true for the persistent argument. Now, when this ReceiverTask object is registered with the port, it will not automatically unregister itself and therefore it will be used to process all items currently in the Port's queue as well as new items that get posted to the Port. So, after activating this new ReceiverTask object, "StringB" appears in the console window, and all the items posted to the Port via the for loop also appear in the console window.
Arbiter.Activate(dq,
    Arbiter.Receive(true, stringPort, 
        delegate(String s) { Msg("Handling string={0}", s); }));
for (int i = 0; i < 10; i++) {
    stringPort.Post("String #" + i.ToString());
}
MultipleItemDemo The MultipleItemDemo method demonstrates how to process Port items in batches. In this method, I first construct a Port<String> object. Then I call Arbiter's MultipleItemReceive method to create a ReceiverTask object that knows how to process multiple items. The MultipleItemReceive method is similar to the Receive method except you also pass to it the number of items that must be posted to the Port object before processing any of them and the callback delegate must identify a method that takes an array of items instead of a single item:
private static void MultipleItemDemo(DispatcherQueue dq) {
    Port<String> stringPort = new Port<String>();
    Arbiter.Activate(dq,
        Arbiter.MultipleItemReceive(true, stringPort, 10, 
            delegate(String[] strings) { 
                Msg("Ten strings={0}", String.Join(", ", strings)); }));

    for (int i = 0; i < 50; i++) stringPort.Post(i.ToString());
    HitEnter();
}
In my example, I passed 10 to the MultipleItemReceive method and so the delegate will get called once for every 10 items posted to the Port. In this example, for variety, I activated the ReceiverTask object with the Port before I posted any items into it. I then have a for loop that posts 50 items, causing the callback to execute five times. When I run this demo, I get the following output:
ThreadID=12: Ten strings=0, 1, 2, 3, 4, 5, 6, 7, 8, 9
ThreadID=12: Ten strings=10, 11, 12, 13, 14, 15, 16, 17, 18, 19
ThreadID=12: Ten strings=20, 21, 22, 23, 24, 25, 26, 27, 28, 29
ThreadID=12: Ten strings=30, 31, 32, 33, 34, 35, 36, 37, 38, 39
ThreadID=12: Ten strings=40, 41, 42, 43, 44, 45, 46, 47, 48, 49
Hit <Enter> to continue demo
At this point, you should have a pretty good sense of how the pieces fit together. You should also have a sense of the kinds of coordination the CCR allows you to pull off. The examples so far have been pretty basic and all of them have been using just one Port object at a time. When you really start working with CCR, you'll see that you can get quite sophisticated and you'll frequently be coordinating items from multiple Port objects together.
AsyncStreamDemo The AsyncStreamDemo method, shown in Figure 5, demonstrates how to read data from a file asynchronously. In this method, I first construct a FileStream object passing a file that I intend to read data from and the FileOptions.Asynchronous flag. The FileStream class already offers methods to perform asynchronous I/O: BeginRead/EndRead and BeginWrite/EndWrite. I have written some adapter methods that can map or translate methods that use the CLR's Asynchronous Programming Model (APM) into the CCR's programming model. My adapter methods are all defined in ApmToCcrAdapters that accompanies the source code for this column.
Basically, when you initiate an asynchronous Read operation on a Stream object, there are two possible results: the I/O can complete returning the number of bytes read (as an Int32) or the operation can fail, indicated with an Exception. So, in my AsyncStreamDemo method, I create a Port<Int32> variable (called bytesReadPort) and a Port<Exception> variable (called failurePort). Both variables are initialized to null.
Next I call my ApmToCcrAdapters type's static Read method. This Read method takes a reference to any Stream-derived object as its first argument. I pass in the arguments that you would normally pass to Stream's Read method: buffer, offset, and count. Then I pass in the reference to the two Port objects. My ApmToCcrAdapters type's static Read method internally constructs the two Port objects and then it starts the asynchronous I/O operation by calling the Stream's BeginRead method.
When the I/O operation completes, an internal callback method (provided by my adapter code) is invoked which checks the result. If the I/O completes successfully, then the number of bytes read (returned from Stream's EndRead method) is posted into the Port<Int32> object. If the I/O completes unsuccessfully, the internal method calls EndRead which throws an exception. The internal method catches this exception and posts the Exception-derived object it into the Port<Exception> object. So, for any single I/O operation only one of the two Port objects will have anything posted into it.
Now I need to tell the CCR how to coordinate the possible results. To do this, I call Arbiter's Choice method, which tells the CCR that only one of the ReceiverTask objects passed to it should be executed. In my example, the first call to Arbiter's Receive method tells the CCR what to execute should the asynchronous I/O operation complete successfully—its anonymous method displays the number of bytes read and the data read from the stream as well. The second call to Arbiter's Receive method tells the CCR what to execute should the asynchronous I/O operation complete unsuccessfully—the Exception object's message is displayed.
Note that when using Arbiter's Choice method, all of the ReceiverTask objects you pass to it must have false passed for their persistent argument. That is, Choice is designed to select one of the specified ReceiverTask objects and then all the ReceiverTask objects are effectively unregistered from their respective Port objects. This is how the CCR ensures that only one ReceiverTask object is chosen (which is what Choice is all about).
AsyncStreamPortSetDemo The AsyncStreamPortSetDemo method demonstrates how to accomplish the exact same job as shown in the AsyncStreamDemo using slightly less code. Because it is so common to use two Port objects together—one for success and the other for failure—the Microsoft.Ccr.Core namespace defines a PortSet<T0, T1> class:
PortSet<Int32, Exception> streamReadPortSet =
    ApmToCcrAdapters.Read(fs, data, 0, data.Length);
Arbiter.Activate(dq, Arbiter.Choice(streamReadPortSet,
    delegate(Int32 bytesRead) {
        Array.Resize(ref data, bytesRead);
        Msg("Read completed, bytes read={0}, data follows:{1}{2}", 
            data.Length, Environment.NewLine, 
            Encoding.ASCII.GetString(data)); 
    },
    delegate(Exception e) { Msg("Read failed, error={0}", e.Message); 
}));
In my AsyncStreamPortSetDemo method, I define a PortSet<Int32, Exception> variable (called streamReadPortSet) and in my ApmToCcrAdapters class I have an overload of the Read method that internally constructs a PortSet (and its Port object members) and returns a reference to this PortSet object.
The Arbiter class also has an overload of the Choice method that takes a PortSet<T0, T1> and allows you to pass two delegates to it. This overload of the Choice method internally calls the Choice method shown and discussed in the AsyncStreamDemo method section. The new code is simpler than the code shown in the AsyncStreamDemo method because you have one PortSet object instead of two Port objects and because Choice takes two delegates instead of having to call Arbiter's Receive method twice passing in a bunch of arguments.
I will use the PortSet in many of the following examples. I should also point out that the CCR defines several PortSet types with variations going all the way to PortSet<T0, T1, ..., T19>. However, PortSet<T0, T1> is by far the most commonly used.
SerialAsyncDemo The SerialAsyncDemo method demonstrates how to use a C# iterator to easily write code that performs several asynchronous operations sequentially (see Figure 6). While these operations are performed sequentially, no threads are blocked. This gives us the syntactical simplicity for sequential programming while allowing the application to scale to support hundreds of thousands of pending operations.
The SaveWebSiteToFile method is a C# iterator method that first requests HTML data from a Web server and then writes this data to a file. To start the operation, I call Arbiter's static FromIteratorHandler method passing it the name of the C# iterator method.
Internally, the CCR will call into the iterator. Inside the iterator, I call one of my adapters to make an asynchronous Web request using a WebRequest object. My adapter returns a PortSet<WebResponse, Exception> indicating the two possible results. Then Choice is used to tell the CCR how to deal with each of the results. The Choice method returns an ITask object that is yield-returned back to the CCR. The CCR then activates this on the Port objects and the CCR's thread returns to the pool waiting for the I/O operation to complete.
If the Web request fails, a message is displayed and yield break executes telling the CCR that there are no more operations to be done. If the Web request completes successfully, a message is displayed indicating that the application got the Web data and then the iterator starts an asynchronous Write operation against a FileStream. Again, the result of the Choice method is yield-returned from the iterator back to the CCR, which then activates it.
If the write request completes, you'll either see a success or failure message and then the FileStream object will be closed. When the iterator exits, the CCR knows not to activ