อยากใช้ go routine กับ channel แบบ ไม่ fix จำนวน

โจทย์ของผมคือ ปกติการใช้ go routine + channel แบบง่ายๆที่เริ่มทำใหม่ๆ จำเป็นจะต้องรู้จำนวนของ go routine ที่ชัดเจน เพื่อเวลาที่เราจะจับของใส่ chan หรือจะเอาออกมา จะได้ทำได้ถูกตัว และครบจำนวน
ผมอยากให้มัน dynamic แบบที่ไปตัดสินใจว่าจะแตก go routine กี่ตัวกันใน runtime เลย จะทำอย่างไร จึงเกิด code ชุดนี้ขึ้นมา

https://github.com/golians/queue

Use case คือ ถ้ามีข้อมูลจำนวนมากเป็น slice/array แล้วต้องการแตก go routine ออกไปช่วยกันทำงาน โดยต้องไม่ทำงานซ้ำกัน และต้องทำให้ครบทุกงาน
เมื่อเริ่มใช้ queue ด้วยคำสั่ง q := NewQueue(100) คือการระบุจำนวนของ record ที่ต้องการทำ จังหวะนี้ Queue จะสร้าง buffer channel จำนวน  100 ตัวแล้วใส่เลข 0-99 ลงไป แล้วปล่อย go routine ออกไปตัวหนึ่ง มีหน้าที่ส่งของให้ channel อีกตัว ที่มี buffer แค่ 1

จากนั้น เวลาจะเรียก queue ก็ใช้คำสั่ง q.Pop() ก็จะได้ chan int ออกมาตัวหนึ่ง มีท่าหล่อๆแบบนี้

 

package main

import (
	"fmt"

	"github.com/pallat/queue"
)

type lot struct {
	items  []int
	count  chan struct{}
	notify chan struct{}
}

func (l *lot) do(i <-chan int) {
	for {
		fmt.Println(l.items[<-i])
		l.count <- struct{}{}
		if len(l.count) == len(l.items) {
			l.notify <- struct{}{}
			return
		}
	}
}

func (l *lot) end() <-chan struct{} {
	return l.notify
}

func main() {
	l := lot{
		count:  make(chan struct{}, 10),
		items:  []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
		notify: make(chan struct{}),
	}

	q := queue.NewQueue(len(l.items))

	go l.do(q.Pop())
	go l.do(q.Pop())
	go l.do(q.Pop())
	go l.do(q.Pop())

	<-l.end()
	fmt.Println("total:", len(l.count))
}

 

 

ประโยชน์ของ queue ตัวนี้คือ จะปล่อย go routine กี่ตัวก็ได้ โดยใส่ q.Pop() ลงไป ใครทำงานเสร็จก่อนก็จะได้ queue ลำดับถัดไปทำงานเอง

ใครส่งใสตรงไหนก็ถามกันได้นะครับ หวังว่าจะเป็นประโยชน์สำหรับคนที่กำลังหาโซลูชั่นแบบนี้ครับ