The PipeBuilder provides us with a fluent syntax for creating pipes. While we can use the constructors on pipes directly, this class should simplify the work. Here are some examples:
var basicPipe = PipeBuilder.New.BasicPipe<string>().Build();
var capacityPipe = PipeBuilder.New.CapacityPipe<string>().WithCapacity(100).Build();
var eitherInletPipe = PipeBuilder.New.EitherInletPipe<string>().WithPrioritisingTieBreaker(Priority.Right).Build();
The generic types above specify the type of messages processed by the pipe.
**Note:**Some pipes might have multiple types if their inlets / outlets expose different types of messages.
Pipes have Inlets and Outlets. We send messages down inlets, and we receive messages from outlets. For example:
// Send a message into the pipe to be picked up by a "receiver"
capacityPipe.Inlet.Send("Hello");
// Receive a message from the pipe that was sent by a "sender"
var message = capacityPipe.Outlet.Receive();
message.Should().Be("Hello");
We can connect pipes together to achieve unique pipe systems to suit our needs. For example:
basicPipe.Outlet.ConnectTo(capacityPipe.Inlet);
capacityPipe.Outlet.ConnectTo(eitherInletPipe.LeftInlet);
basicPipe.Inlet.Send("Travelled a long way");
var travelledMessage = eitherInletPipe.Outlet.Receive();
travelledMessage.Should().Be("Travelled a long way");
Inlets can be connected to outlets allowing us to build a "network" of pipes called a pipe system.
The end result is that this pipe system exposes some inlets and outlets to the outside world - a bunch of interacting services / threads.
Each service can focus on just sending or receiving messages down the inlets and outlets it was passed, without worrying about the design of the system as a whole.
The pipe system should be constructed where it makes sense to know how the application is connected together. For web developers, this resembles the role of the Composition Root.
The behaviour of individual pipes is explained in Specifics.
That's essentially all there is to it! Pipes, their inlets and their outlets are all customisable, so it's a little tricky to describe exactly what they do without going into specifics, but normally:
- Any number of threads can try to receive from the same outlet.
- Any number of threads can try to send down the same inlet.
- Most pipes force a sending thread to wait for a receiving thread to be available and vice versa.
- The whole pipe system is thread safe.
Note: When multiple threads interact with the same in/outlet, they are held in a queue to ensure "fairness".
To continue to learn pipes, I'd recommend you check out some of the worked examples:
- Composite Pipe Example - most of what you need to know. Includes:
- Creating a pipe system.
- Creating a new pipe.
- Extending the build syntax.
- New Pipe Example - more advanced. Includes:
- How to create a pipe not made up of other pipes (unique behaviour).
- Some explanation of how pipe systems work internally.