DEV Community

Tony Metzidis
Tony Metzidis

Posted on • Edited on

Go Nuts with Channels: Prioritize a Channel with Middleware

update Added a deadline

Channels are great because you can easily rewire them from fifos into multiplexers , funnels and fan-outs.

In this case we want to implement a priority fifo-- the P could be a TTL or value indicator.

Not much is needed, just a custom sort.Interface impl. based on your ordering field P--for priority. Then just read inChan into the buf, sort , and write to outChan

package main

import (
    "fmt"
    "math/rand"
    "sort"
    "time"
    "strconv"
    "context"
)

type PriorityStruct struct {
    P    int
    name string
}

const BUFSIZE = 10

type PriorityChan chan PriorityStruct
type PriorityBuf []PriorityStruct

func (s PriorityBuf) Len() int {
    return len(s)
}
func (s PriorityBuf) Swap(i, j int) {
    s[i], s[j] = s[j], s[i]
}
func (s PriorityBuf) Less(i, j int) bool {
    return s[i].P < s[j].P
}

func Sender(pchan PriorityChan) {
    for {
        randNum := rand.Intn(3)
        pchan <- PriorityStruct{
            P:    randNum,
            name: "Bob-" + strconv.Itoa(randNum),
        }
        time.Sleep(10 * time.Millisecond)
    }
}

func Prioritize(ctx context.Context, inChan PriorityChan, outChan PriorityChan) {
    for {
        buf := make(PriorityBuf, BUFSIZE)
        var i int
        for i = range buf {
            select{
            case buf[i] = <-inChan:
            case <-ctx.Done():
                break
            }
            buf[i] = <-inChan
        }
        sort.Sort(buf)
        for j:= 0; j <= i; j++ {
            outChan <- buf[j]
        }
    }
}

func Receiver(rchan PriorityChan) {
    for {
        fmt.Printf("%v\n", <-rchan)
    }
}

func main() {
    inChan := make(PriorityChan)
    outChan := make(PriorityChan)

    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(50 * time.Millisecond))
    defer cancel()
    go Sender(inChan)
    go Receiver(outChan)
    go Prioritize(ctx, inChan, outChan)
    <- time.After(5 * time.Second)
}

Enter fullscreen mode Exit fullscreen mode

Run it

go run priority-channel.go |head -n 15
{0 Bob-0}
{0 Bob-0}
{0 Bob-0}
{1 Bob-1}
{1 Bob-1}
{1 Bob-1}
{2 Bob-2}
{2 Bob-2}
{2 Bob-2}
{2 Bob-2}
{0 Bob-0}
{0 Bob-0}
{1 Bob-1}
{1 Bob-1}
{2 Bob-2}
Enter fullscreen mode Exit fullscreen mode

Check it out: https://play.golang.org/p/wWgi5rPcIwb

If you're not on go-nuts -- it's a good community of go-users .

Top comments (0)