The document introduces the Concurrency and Coordination Runtime (CCR) library. CCR makes asynchronous programming simpler than writing threaded code by expressing concurrent programs using explicit continuation passing style. It discusses how CCR uses dispatchers, dispatcher queues, ports, and arbiters to coordinate asynchronous operations. Examples are provided of how to use choice, interleave, and join arbiters to process messages from multiple ports. Iterators are also introduced as a way to specify asynchronous workflows without continuation passing style.
2. Concurrency: is it necessary?
Concurrent programming is how to make your
application to scale by doing more things at once.
If you have one CPU – you can do only one thing at a
time, but it doesn't mean it is actually DOING
something most of the time - in fact, 99% of the time,
your CPU is literally turned off.
Nowadays more and more machines have more than
one core, these machines CAN do more than one
thing at once.
Don’t overdesign! Not every problem can or should
be solved with a concurrent solution.
3. What is CCR?
Concurrency and Coordination Runtime
CCR is a lightweight distributed services-
oriented architecture and a common
language runtime (CLR)-based library.
The CCR makes programming asynchronous
behavior much simpler than the typical
challenge of writing threaded code.
CCR expresses concurrent programs in
explicit continuation passing (CPS) style
which is often quite an arduous way for a
human to express concurrent calculations.
4. What is CCR?
Has nothing to do with a runtime, though. It’s
a managed library that can be used from any
managed language: C#, VB.NET, etc...
Released (December 12, 2006) as part of the
Microsoft Robotics Studio, but can be used as
a standalone library (check the license!)
Here is a short explanation …
5. A lot of asynchronous events?
Tired of asynchronous code coordination?
Do you see a resemblance?
7. Example (compare APM & CCR)
Need to schedule 2 (or more) asynchronous
operations and, as soon as all of them complete,
execute some action
How to start with CCR?
Add a reference to the Ccr.Core.dll
using Microsoft.Ccr.Core;
Create Dispatcher
Create DispatcherQueue
Create ports
Declare actions (handlers)
8. Dispatcher class
Configurable number of threads – on construction.
Default: 1 thread for every CPU (if 1 CPU – 2 threads).
No dynamically creating or destroying threads.
Possible to set threads' scheduling priority.
Default: normal priority.
Unlike CLR:
No dynamical thread to change number of thread pool
threads based on the workload – streamlined, simple
and high-performance code.
No limitation on number of thread pools (dispatcher
instances) with different thread priorities for different
kind of tasks.
For debugging – gives a name for each thread.
9. Dispatcher Queue
Maintains a queue of delegates that identify
methods ready to execute.
Dispatcher's threads wait for entries to
appear in the DispatcherQueue.
Order is not important (whatever comes first:
events or entries).
Use as many queues as you need.
Unlike CLR: Dispatcher dequeue equally
(round-robin) from all the associated queues.
Useful for different priority tasks.
Default constructor – CLR thread pool
10. Ports and PortSets
First-In-First-Out (FIFO)
Possible to instantiate a port with up to 16 different
types. Port (that carries multiple values) works by
maintaining unrelated sub-ports for each type.
PortSet<int,string> pIS = new PortSet<int,string>();
Reading an int value from pIS = reading from the sub-
port that contains ints. This read operation is
unaffected by the state of the string sub-port.
Test() to atomically remove a value from a port (if the
port is not empty).
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.
11. CCR Architecture
Items are posted into a Port object.
Registered ReceiverTasks send the items to their arbiters,
which decide what callback method should ultimately execute to
process the posted item.
The Arbiter then queues the work item task into a
DispatcherQueue, and a Dispatcher (or CLR thread pool) thread
will execute the method processing the posted item.
12. Arbiter
Arbiter is a static class that defines factories
that create other Arbiters
In fact each constructed Arbiter contains a
reference to one or more constructed
ReceiverTask objects
You must activate the arbiters by calling
Arbiter's Activate method. This will register 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.
13. Single Item Receiver Handler
Port<int> p = new Port<int>();
Arbiter.Activate (dispatcherQueue,
Arbiter.Receive(false, p, delegate (int i)
{
// … do something …
Console.WriteLine(i) ;
}) );
p.Post(10);
Output:
10
14. Persisted Arbiters
Some Arbiters can be persisted or not
Choice and Interleave's
TeardownReceiverGroup require that their
arbiters always be non-persistent
If you forget to activate your arbiters, items
will continue to queue up in Port objects but
they will NEVER get processed!
15. Multiple Item Receiver Handler
Will be called only when the configured number of
items has accumulated on the associated port or one
item across multiple ports.
An array of items will be passed atomically to the
handler.
Arbiter.MultipleItemReceive<T0>(bool persist, Port<T0> port, int messageCount,
VariableArgumentHandler<T0> handler);
Arbiter.MultipleItemReceive<T0,T1>(PortSet<T0,T1> port,
Handler<ICollection<T0>, ICollection<T1>> handler);
Arbiter.MultiplePortReceive<T0>(bool persist, Port<T0> [] ports,
VariableArgumentHandler<T0> handler);
16. Example: Hello World! in Math
(Matrix Multiplication)
From site: Scalar and Matrix Multiplication
17. Data Flow Diagram
portMatrix1 portMatrix2 portSum portResult
Result
+
Multiply * Sum
handler handler
Simplified graphical representation of the data
flow in the Matrix multiplication example
18. Example: racing conditions
STOP! There is a catch!
Change the number of threads for the Dispatcher to 0
(it’ll use a default number of worker threads)
Result is changing all the time
Dispatcher is providing a real scheduling of work
items across multiple CPUs and Threads
Order is unpredictable !!!
Never count on it !!!
19. Common Arbiters
single item receivers for specifying handlers
on a single port
a choice arbiter which will chose which of two
receivers to fire based on the availability of
messages
an interleave arbiter (explained later)
a join arbiter for static join expressions
MultipleItemReceive arbiters for specifying
dynamic joins
20. Predefined Arbiters (partial list)
FromHandler Just executes a method (not associated
with a port)
FromIteratorHandler Execute until enumerator is done (not
associated with a port)
Receive Process a single item from a port
MultipleItemReceive Process a bunch of items at once
MultiplePortReceive Process a single item from multiple ports
(one item per port)
JoinedReceive Process multiple items from 2 ports (one
item per port)
Choice Executes one & only one method from a
set of methods
Interleave Calls back a set of methods to process
items from various ports. Arbiter is similar
to a reader/writer thread synchronization
lock
21. Choice Arbiter Handler
Arbiter.Choice(PortSet<T0, T1> resultPort, Handler<T0> h0, Handler<T1> h1);
Arbiter.Choice(params ReceiverTask[] receivers);
The choice arbiter allows one to specify that one of two (or
more) branches should fire and the others should be discarded.
For example using the first method:
Example:
Arbiter.Activate(taskQueue,
Arbiter.Choice(
Arbiter.Receive(false, port1, MyIntHandler),
Arbiter.Receive(false, port2, MyStringHandler)
)
);
Will run either MyIntHandler or MyStringHandler but not both.
The choice will be determined by the availability of int or string
message on the ports and if both types of messages are
available then a non-deterministic choice is made.
22. Join Arbiter Handler
Arbiter.Activate(dispatcherQueue,
Arbiter.JoinedReceive<int,string>(true, p1,p2, IntAndStringJoinedHandler) );
The above will schedule the execution of the
IntAndStringJoinedHandler when values can be
atomically read on ports p1 AND p2 and will
continue doing so as long as messages are available
(in this example its a persisted receiver). The handler
will be passed the values that were read. One can
join over several ports of different types.
MultiportReceive is a form of a Join Arbiter:
Arbiter.MultiplePortReceive<T0>(bool persist, Port<T0> [] ports,
VariableArgumentHandler<T0> handler);
23. Handler
Interleave Arbiter
Handler
Handler
Arbiter.Interleave(
TeardownReceiverGroup teardownGroup,
ExclusiveReceiverGroup exclusiveGroup,
ConcurrentReceiverGroup concurrentGroup
);
Expresses concurrent calculations
Corresponds to multiple reads but single writers to a shared
state. Its a more high level, far more concise way to capture
advanced protection schemes.
The one time (non-persistent) shutdown handler (any one from
the TeardownReceiverGroup) will cause the entire interleave to
stop, guaranteeing no delegates are running or will ever run
again. Makes clean up easy!
Why not to use join?
Interleave is error prone for this kind of operations.
24. Interleave Arbiter: continues
Teardown will occur when all handlers have
stopped executing (it has strong guarantees).
It will not wait until all queued messages have
executed, only just when all handlers
currently running are done. At least that is the intent.
The interleave arbiter is biased towards
exclusive handlers and the teardown group,
so any more queued messages will not be
processed the moment an exclusive or
teardown message is received.
25. Iterators: no more CPS
Arbiter.Activate(dq, Arbiter.FromIteratorHandler(SaveWebSiteToFile));
IEnumerator<ITask> SaveWebSiteToFile() { … }
Arbiter.Activate(dq,Arbiter.ReceiveWithIterator(false,port,DoWorkHandler));
IEnumerator<ITask> DoWorkHandler(DoWork w) { … }
Inheriting from the IEnumerator interface allows one
to specify "yield" points in a method at which control
flow returns to the caller. When a MoveNext() method
is called on such a method control flow resumes from
the “yield” point, not from the start of the method.
The coordination primitives in CCR all inherit from the
ITask interface and this is used to help identify the
iteration variable for use with the IEnumerator
interface.
26. Iterator: example
public void Start()
{
Arbiter.Activate(taskQueue, Arbiter.ReceiveWithIterator(false, pMain, DoWorkHandler));
}
IEnumerator<ITask> DoWorkHandler(DoWork w)
{
DoWork workA = new DoWork();
Result r = null;
// send message to ServiceA
pServiceA.Post(workA);
// yield for result on response port attached to outbound message
yield return Arbiter.Choice(workA.pResponse,
delegate (Result Res) { r = Res;},
delegate (Failure F) { Trace.WriteLine("Failed");} );
// when either branch above happens, we will continue executing
// here without ever having blocked a thread!
if (r == null) yield break; // error occurred, break iteration
// send next message, depending on the result from A
DoWork workB = new DoWork(r);
pServiceB.Post(workB);
// ... handle result form B etc.
}
27. More on iterators
Schedule a task that will iterate over some
message coordination
dispatcherQueue.Enqueue(new IterativeTask<int>(message, MyIntIterator));
IEnumerator<ITask> MyIntIterator(int message)
{
Console.WriteLine(“got: {0}”, message); yield break;
}
You can also associate a Reissue receiver
with a port and an iterator Handler
Arbiter.Activate(dispatcherQueue,
Arbiter.ReceiveWithIterator(true, port, DoWorkHandler));
28. Enumerator Handler limitations
static IEnumerator<ITask> Handler()
{
yield return Arbiter.FromHandler(delegate()
{ Console.WriteLine("I got here..."); });
yield break;
} BAD!
You can’t yield to any handler. You must yield to a receive, a
choice or a join (or multiple item receive).
This is a limitation of the current version of the CCR. To get
around this you can of course create a simple port, post and
then yield return:
Port<bool> port = new Port<bool>();
port.Post(true);
yield return Arbiter.Receive(false,port,delegate(bool b){ … });
29. Don’t starve
Very hard to locate a blocked or misbehaving thread
in the pool (very easy in GUI – only one thread)
System.Threading.Timer is a convenient way to shoot
yourself in the foot – all instances are using the same
thread pool (size: 50)
Few simple guidelines to avoid starvation:
Avoid using blocking I/O in an event handler.
Split individual, long-running computations into several
shorter ones.
Choose useful intervals for timer-driven behavior.
Serialize timer events of the same activity.
30. Timeouts – don’t hang forever
Never forget about Timeouts
Tell the DispatcherQueue to wait and then to post the
current date and time into the timeoutPort:
Port<DateTime> timeoutPort = new Port<DateTime>();
dq.EnqueueTimer(TimeSpan.FromMinutes(2), timeoutPort);
Arbiter.Receive(false, timeoutPort,
delegate(DateTime dt)
{
// … do stuff …
});
32. Web Crawler – problem definition
Create a smart web crawler that will
download images and the pages themselves
recursively from the web.
Flush the downloaded content to your hard
drive.
Download requests should timeout if hanged.
Code should be 100% asynchronous.
33. Web Crawler – non-goals
Limit the depth of the recursion with a
configurable parameter.
Download only pages descendants of the root
site.
Save each page (and all its images) into it’s
own folder.
Preserve the relationship of the pages, while
writing to the disk, as nested folders.
Show the progress on the Windows form
without affecting the user’s experience (non-
blocking display).
34. Web Crawler – the strategy
Provide a continuous stream of URLs
Interleave?
FromIteratorHandler?
Better: persistent Receive handler
Each URL request will either succeed, fail or
timeout
Definitely the Choice arbiter with timeout port
As soon as download completed – schedule
the asynchronous write
Choice arbiter – success or failure
35. Web Crawler – code & demo
YES !!!
Unbelievable !!!
IT WORKS!!!
IT’S EASY!!!
36. What about WinForms?
Use Microsoft.Ccr.Adapters.WinForms
ManualResetEvent MainThreadEvent = new ManualResetEvent(false);
// Bind dispatcher queue to WinformsAdaptor
WinFormsServicePort port = WinFormsAdapter.Create(dispatcherQueue);
port.Post(new RunForm(delegate
{
form = new WebCrawlerForm(port);
form.HandleDestroyed += delegate(object sender, EventArgs e)
{ MainThreadEvent.Set(); };
return form;
}));
// Wait for a “shutdown” signal
MainThreadEvent.WaitOne();
// We need to inform the WinFormsAdaptor that it is about to quit.
port.Post(new Shutdown());
// Let the system to finalize everything
System.Threading.Thread.Sleep(500);
37. Causality ~ <try/catch>
Causalities are a generalization of try/catch, across
threads and processors, of the nested exception
handling mechanism. Much more powerful however
since it can deal with Joins (two different causalities
coming together in a common handler).
They are NOT transactions
When an exception is thrown, within any logically
related descendant of a Post operation, that
exception can be cleanly caught and dealt within one
place.
The CoordinationPort allows anyone within a
causality to post a message to the "owner" of the
causality, allowing you to implement all kinds of
internal communication protocols.
38. Causality: example
Port<Exception> pException = new Port<Exception>();
Arbiter.Activate(dispatcherQueue, Arbiter.Receive(false, pException,
/* ExceptionHandler */ delegate(Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.ToString());
}));
Arbiter.Activate(dispatcherQueue, Arbiter.FromHandler(delegate
{
// Define Causality scope (owner)
Dispatcher.AddCausality(new Causality("test", pEx));
// … do some stuff … that spawn AsynchronousHandler …
}
void AsynchronousHandler()
{
// … do some more stuff that throws exception
throw new Exception(“bad things happen …”);
}
39. Advanced CCR: the next steps
The observer pattern (what is called pub/sub or
publication/subscription) can be implemented using
the Subscription service. The service (implemented
using the CCR) keeps a list of ports, and then posts
the message N items (one for each subscriber).
Service’s can be observed, used across
nodes/machines. This keeps the CCR simple.
See Service Tutorials 4,5,6 for more info.
If a service is overkill in your scenario, you can write
a simple class that does this: keeps a list of ports and
replicates messages on them. Or you can write a
simple arbiter (derive from Receiver) that does this.
Service orchestration …
40. Meet BOB (version 1)
Robot (self programmable), controlled by the
CCR (running on Mobile device - PDA)
Communicates with a main computer to
upload gathered information and to get new
instructions
41. MS Robotics Studio (MSRS)
Writing an application using Microsoft Robotics Studio is a simple
matter of orchestrating input and output between a set of services.
Services represent the interface to software or hardware and
allow you to communicate between processes that perform
specific functions.
42. Simulation Runtime
Simulation runtime can be used in a variety of advanced
scenarios with high demands for fidelity, visualization and scaling.
At the same time a novice user can use simulation with little to no
coding experience and develop interesting applications.
43. VPL – Visual Programming Language
Application development environment designed on a
graphical dataflow-based programming model
44. Conclusion
The CCR is a CLR library that provides a consistent
and scalable way to program asynchronous
operations and coordinate among multiple responses.
Framework Class Library (FCL) classes, that already
support the CLR's asynchronous programming model
(such as Stream's BeginRead and BeginWrite
methods) can be easily wrapped, allowing existing
types to integrate with the CCR so that complex
failure handling and coordination patterns can be
coded in a robust, reliable and concise way.
The use of C# iterators for scheduling operations
allows sequential programming without blocking OS
threads, thus enabling scaling without sacrificing the
simplicity of sequential code.
45. Acronyms
CCR – Concurrency and Coordination
Runtime
APM – Asynchronous Programming Model
CPS – Continuous Passing Style
CLR – Common Language Runtime
FCL – Framework Class Library
MSRS – Microsoft Robotics Studio
46. Links
CCR Wiki
http://channel9.msdn.com/wiki/default.aspx/Channel9.ConcurrencyRuntime
CCR Patterns
http://channel9.msdn.com/wiki/default.aspx/Channel9.CcrPatterns
MSRS Home
http://msdn.microsoft.com/robotics/
My e-mail: igormoochnick@yahoo.com
My new Blog: http://igorshare.blogspot.com/
My new Web Site:
http://igor.moochnick.googlepages.com/