Nyx by Example

Producer-Consumer

The producer-consumer pattern decouples data generation from processing. The bounded channel (capacity 3) provides natural backpressure: the producer blocks when the buffer is full.

Code

// Producer-consumer pattern with bounded channel
// Patrón productor-consumidor con canal acotado

var items_ch: Map = Map.new()

fn produce() -> int {
    var i: int = 0
    while i < 5 {
        let item: int = (i + 1) * 10
        print("produced: " + int_to_string(item))
        channel_send(items_ch, item)
        i = i + 1
    }
    channel_send(items_ch, -1)
    return 0
}

fn consume() -> int {
    while true {
        let item: int = channel_recv(items_ch)
        if item < 0 { return 0 }
        print("consumed: " + int_to_string(item))
    }
    return 0
}

fn main() -> int {
    // Bounded channel — producer blocks if buffer is full
    items_ch = channel_new(3)

    let p: int = thread_spawn(produce)
    let c: int = thread_spawn(consume)

    thread_join(p)
    thread_join(c)
    channel_destroy(items_ch)
    print("pipeline complete")
    return 0
}

Output

produced: 10
produced: 20
produced: 30
produced: 40
produced: 50
consumed: 10
consumed: 20
consumed: 30
consumed: 40
consumed: 50
pipeline complete

Explanation

The channel items_ch has a capacity of 3, meaning the producer can send at most 3 items before blocking. This provides natural backpressure -- if the consumer is slow, the producer automatically slows down to match.

The producer generates values 10, 20, 30, 40, 50 and sends them through the channel, followed by a -1 sentinel to signal completion. The consumer loops on channel_recv, processing each item until it sees the sentinel.

The exact interleaving of "produced" and "consumed" messages depends on scheduling, but the order within each stream is always preserved. The bounded buffer prevents the producer from overwhelming the consumer with data.

← Previous Next →

Source: examples/by-example/60-producer-consumer.nx