using System.Threading.Channels;
using System.Threading.Tasks;
using System.Collections.Generic;
public string CustomerName { get; init; }
public string DrinkType { get; init; }
public Channel<string> Complete { get; init; }
private readonly Channel<DrinkOrder> _orders = Channel.CreateUnbounded<DrinkOrder>();
private readonly Channel<DrinkOrder> _grinding = Channel.CreateBounded<DrinkOrder>(1);
private readonly Channel<DrinkOrder> _espresso = Channel.CreateBounded<DrinkOrder>(2);
private readonly Channel<DrinkOrder> _steaming = Channel.CreateBounded<DrinkOrder>(2);
private readonly Channel<DrinkOrder> _finished = Channel.CreateUnbounded<DrinkOrder>();
private async Task GrindBeansAsync()
while (await _grinding.Reader.WaitToReadAsync())
if (await _grinding.Reader.ReadAsync() is DrinkOrder order)
Console.WriteLine($"Grinding beans for {order.CustomerName}'s {order.DrinkType}");
if (order.DrinkType == "Filter")
await _finished.Writer.WriteAsync(order);
await _espresso.Writer.WriteAsync(order);
private async Task MakeEspressoAsync()
while (await _espresso.Reader.WaitToReadAsync())
if (await _espresso.Reader.ReadAsync() is DrinkOrder order)
Console.WriteLine($"Making espresso for {order.CustomerName}'s {order.DrinkType}");
if (!NeedsMilk(order.DrinkType))
await _finished.Writer.WriteAsync(order);
await _steaming.Writer.WriteAsync(order);
private async Task SteamMilkAsync()
while (await _steaming.Reader.WaitToReadAsync())
if (await _steaming.Reader.ReadAsync() is DrinkOrder order)
Console.WriteLine($"Steaming milk for {order.CustomerName}'s {order.DrinkType}");
await _finished.Writer.WriteAsync(order);
private async Task ServeOrdersAsync()
while (await _finished.Reader.WaitToReadAsync())
if (await _finished.Reader.ReadAsync() is DrinkOrder order)
Console.WriteLine($"✓ {order.CustomerName}'s {order.DrinkType} is ready!");
await order.Complete.Writer.WriteAsync("done");
private async Task ProcessOrdersAsync()
while (await _orders.Reader.WaitToReadAsync())
if (await _orders.Reader.ReadAsync() is DrinkOrder order)
Console.WriteLine($"Starting {order.CustomerName}'s {order.DrinkType}");
await _grinding.Writer.WriteAsync(order);
_grinding.Writer.Complete();
_espresso.Writer.Complete();
_steaming.Writer.Complete();
_finished.Writer.Complete();
private bool NeedsMilk(string drinkType) => drinkType switch
public async Task StartAsync(int grinders = 1, int espressoMachines = 2, int steamers = 2)
for (int i = 0; i < grinders; i++)
for (int i = 0; i < espressoMachines; i++)
for (int i = 0; i < steamers; i++)
_ = ProcessOrdersAsync();
public async Task OrderDrinkAsync(string customerName, string drinkType)
var complete = Channel.CreateUnbounded<string>();
var order = new DrinkOrder
CustomerName = customerName,
Console.WriteLine($"{customerName} ordered a {drinkType}");
await _orders.Writer.WriteAsync(order);
await complete.Reader.ReadAsync();
public static async Task Main()
var shop = new CoffeeShop();
await shop.StartAsync(grinders: 1, espressoMachines: 2, steamers: 2);
var orders = new List<Task>
shop.OrderDrinkAsync("Alice", "Latte"),
shop.OrderDrinkAsync("Bob", "Cappuccino"),
shop.OrderDrinkAsync("Charlie", "Filter"),
shop.OrderDrinkAsync("Dave", "Black Coffee"),
shop.OrderDrinkAsync("Eve", "Latte Macchiato")
await Task.WhenAll(orders);
Console.WriteLine("All orders complete!");