How to: Create Reusable Agents
Applies to: Functional Programming
Published: January 2010
Summary: This group of articles discusses the development of agents that can be easily combined to solve recurring tasks in concurrent server-side programming.
This topic contains the following sections.
This article is associated with Real World Functional Programming: With Examples in F# and C# by Tomas Petricek with Jon Skeet from Manning Publications (ISBN 9781933988924, copyright Manning Publications 2009, all rights reserved). No part of these chapters may be reproduced, stored in a retrieval system, or transmitted in any form or by any means—electronic, electrostatic, mechanical, photocopying, recording, or otherwise—without the prior written permission of the publisher, except in the case of brief quotations embodied in critical articles or reviews.
An agent-based application usually combines two kinds of agents. The first group are agents that are specific to the application and perform some specialized tasks. This may include various calculations, storing data to an application-specific database, and so on. The second group are agents that perform some generally useful tasks and can be shared by multiple applications. Such agents may, for example, group messages, forward messages with a delay, or transmit messages over network. This section discusses the implementation of two such reusable agents.
Reusable agents are usually generic because they can store any types of values. They can expose the following types of members:
Synchronous methods. This type of member is used for operations that send an input or command to the reusable agent but do not return any results. When the agent needs to respond to the message, the method should be asynchronous. A typical type signature of synchronous methods is 'T -> unit.
Asynchronous methods. When the operation needs to send an input or command to the agent and then reply back to the client, it should be performed asynchronously to avoid blocking threads. These methods are typically implemented using PostAndAsyncRepy and have type signature 'T -> Async<'R>.
Events. Events can be used when the agent needs to report some change in the state or report results immediately as they become available. The type signature of members implemented using events is usually IEvent<'T> or IObservable<'T>.
The following two reusable agents will demonstrate all three types of members.
This article shows how to solve the consumer-producer problem using agents. It implements a queue that blocks the producer when the queue is full and blocks the consumer when the queue is empty.
When receiving messages, the application may want to put them into groups of some maximum size and then process them in batches. This article develops an agent that implements this behavior.
This section discusses how to design two very frequently needed reusable agents. For more information about agents and server-side programming in F#, see the following overviews.
The following tutorial shows how to create a larger server-side application using agents:
To download the code snippets shown in this article, go to http://code.msdn.microsoft.com/Chapter-2-Concurrent-645370c3
This article is based on Real World Functional Programming: With Examples in F# and C#. Book chapters related to the content of this article are:
Book Chapter 13: “Asynchronous and data-driven programming” explains how asynchronous workflows work and uses them to write an interactive script that downloads a large dataset from the Internet.
Book Chapter 14: “Writing parallel functional programs” explains how to use the Task Parallel Library to write data-parallel and task-based parallel programs. This approach complements agent-based parallelism in F#.
Book Chapter 16: “Developing reactive functional programs” discusses how to write reactive user interfaces using asynchronous workflows and events. This approach is related to agents but more suitable for creating user interfaces.