模块和包用于处理更大层面的封装需求,而类、子程序、函数则处理更多细节层面的事情。近些年我发现,类似乎成了开发人员最难封装的结构之一。一个类拥有3000多行的main方法,或者一个类仅有set/get方法用于它的原始属性的情况并不罕见。这些例子说明开发者还没有充分理解面向对象思想,没有发挥对象建模的优势。对于开发者熟悉的术语POJO以及POCO(Plain Old Java/C# Object),这相当于模型的范例,回归到了面向对象的基础——对象是朴实而简单的,但不是哑巴!
functestRunner() { log.Println("Runner test starting work...")
// 新建一个runner,并强制每个并发任务的超时时间为5秒 r := runner.New(3 * time.Second) // 循环创建10个并发任务,并将其丢给runner管理 for i := 0; i < 5; i++ { r.Add(func(id int) { // 这里只是模拟,每个并发任务都是睡眠它自身id的秒数 log.Printf("Processor - Task #%d.\n", id) time.Sleep(time.Duration(id) * time.Second) log.Printf("Task #%d done.\n", id) }) }
// 一次性启动runner内部的全部并发任务 if err := r.Start(); err != nil { switch err { case runner.ErrTimeout: // 当并发任务中有任务执行超时,就立即返回 log.Println("Terminating due to timeout.") os.Exit(1) case runner.ErrInterrupt: // 当程序被ctrl+c时,强制结束所有并发任务 log.Println("Terminating due to interrupt.") os.Exit(2) } }
// Worker 必须满足接口,才能使用工作池 type Worker interface { Task() }
// Pool 工作池,相当于goroutines池管理 type Pool struct { work chan Worker wg sync.WaitGroup }
// New 创建工作池的工厂函数 funcNew(maxGoroutines int) *Pool { p := Pool{ work: make(chan Worker), }
p.wg.Add(maxGoroutines) for i := 0; i < maxGoroutines; i++ { gofunc() { // p.work是通道,所有创建goroutine之后 // for循环会被阻塞,直到p.work被关闭为止 for w := range p.work { w.Task() } p.wg.Done() }() }
return &p }
// Run 提交工作到工作池 func(p *Pool)Run(w Worker) { p.work <- w }
// 启动5个goroutine接收数字 for i := 0; i < 5; i++ { go receive(bufferedChannel) } // 启动5个goroutine发送随机5个数字 for i := 0; i < 5; i++ { go send(bufferedChannel, rand.Intn(100)) }
wg.Wait() close(bufferedChannel) }
//---------------程序输出结果------------ sent number: 81 received number: 81 sent number: 81 sent number: 87 sent number: 47 received number: 81 received number: 87 received number: 47 sent number: 59 received number: 59