Go并发之百万级处理
代码和注释均在代码:
package main import ( "fmt" "runtime" "time" ) //---------------------Job------------------------- type Job interface { Do() } // 一个数据接口,所有的数据都要实现该接口,才能被传递进来 //实现Job接口的一个数据实例,需要实现一个Do()方法,对数据的处理就在这个Do()方法中。 /* Job通道: 这里有两个Job通道: 1、WorkerPool的Job channel,用于调用者把具体的数据写入到这里,WorkerPool读取。 2、Worker的Job channel,当WorkerPool读取到Job,并拿到可用的Worker的时候,会将Job实例写入该Worker的Job channel,用来直接执行Do()方法。 */ //--------------------Worker----------------------- type Worker struct { JobQueue chan Job //Worker的Job通道 } func NewWorker() Worker { return Worker{JobQueue: make(chan Job)} } //每一个被初始化的worker都会在后期单独占用一个协程 //初始化的时候会先把自己的JobQueue传递到Worker通道中, //然后阻塞读取自己的JobQueue,读到一个Job就执行Job对象的Do()方法。 func (w Worker) Run(wq chan chan Job) { go func() { for{ wq <- w.JobQueue select { case job := <- w.JobQueue: job.Do() } } }() } //----------------WorkerPool--------------------- //工作池(WorkerPool): type WorkerPool struct { workerlen int //WorkerPool中同时 存在Worker的个数 JobQueue chan Job // WorkerPool的Job通道 WorkerQueue chan chan Job } //初始化时会按照传入的num,启动num个后台协程,然后循环读取Job通道里面的数据, //读到一个数据时,再获取一个可用的Worker,并将Job对象传递到该Worker的chan通道 func NewWorkerPool(workerlen int) *WorkerPool { return &WorkerPool{ workerlen: workerlen, JobQueue: make(chan Job), WorkerQueue: make(chan chan Job,workerlen), } } func (wp *WorkerPool) Run() { fmt.Println("初始化worker...") for i:=0;i<wp.workerlen;i++ { worker := NewWorker() worker.Run(wp.WorkerQueue) } go func() { for{ select { case job := <-wp.JobQueue: worker :=<-wp.WorkerQueue worker <- job } } }() } //------------------测试-------------------- type Score struct { Num int } func (s *Score) Do() { fmt.Println("num:",s.Num) time.Sleep(time.Second*1*1) } func main() { num := 100*100*20 p := NewWorkerPool(num) p.Run() datanum := 100*100*100*100 go func() { for i:=1;i<datanum;i++{ sc := &Score{Num: i} p.JobQueue <- sc } }() for{ fmt.Println("runtime.NumGoroutine():",runtime.NumGoroutine()) time.Sleep(time.Second*2) } }