当需要在一段时间后执行某个任务时,可以使用 time.Timer。Timer 会在一段时间后向一个 channel 发送一个时间值,可以使用该 channel 来触发任务的执行。具体来说,当需要在一段时间后执行某个任务时,可以创建一个 Timer,然后使用 <-timer.C 语句从 Timer.C channel 中读取时间值,当读取到时间值时,就执行任务。
package main
import (
"fmt"
"time"
)
func main() {
timer := time.NewTimer(2 * time.Second)
defer timer.Stop()
<-timer.C
fmt.Println("timer expired")
}
结果:
timer expired
当需要定期执行某个任务时,可以使用 time.Ticker。Ticker 会定期向一个 channel 发送一个时间值,可以使用该 channel 来触发定期任务的执行。具体来说,当需要定期执行某个任务时,可以创建一个 Ticker,然后使用 for range 循环从 Ticker.C channel 中读取时间值,每当读取到时间值时,就执行定期任务。
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
for {
select {
case <-ticker.C:
fmt.Println("tick")
}
}
}
结果:
tick
tick
tick
tick
...
当需要等待一组 goroutine 完成时,可以使用 sync.WaitGroup。WaitGroup 用于等待一组 goroutine 完成其工作。具体来说,当一个 goroutine 需要等待一组其它 goroutine 完成时,它可以调用 WaitGroup 的 Add 方法来增加计数器的值,然后启动这组 goroutine。每个 goroutine 完成时,都应该调用 Done 方法来减少计数器的值。最后,等待这组 goroutine 完成时,应该调用 Wait 方法,该方法会阻塞直到计数器的值为 0。
基础用法:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("goroutine %d\n", i)
}(i)
}
wg.Wait()
fmt.Println("all goroutines done")
}
这个版本只包含一个生产者和一个消费者,使用goroutine和channel来实现消息传递。
- done变量用于在主线程中阻塞等待所有子协程的结束。在代码执行过程中,通过向 done 变量发送信号(done <- true)来表示一个子协程已完成任务。当所有子协程都完成任务后,执行 <-done 将阻塞主线程,等待所有子协程完成任务。而在 produce 和 consume 中,done 只用于简单的通信任务,不会阻塞主线程。
package main
import (
"fmt"
_ "time"
)
func produce(ch chan<- int, done chan<- bool) {
for i := 1; i < 3; i++ {
ch <- i
fmt.Printf("生产者生产 %d\n", i)
}
done <- true
}
func consume(ch <-chan int) {
for i := range ch {
fmt.Printf("消费者消费 %d\n", i)
}
}
func main() {
ch := make(chan int)
done := make(chan bool)
go produce(ch, done)
go consume(ch)
<-done
}
这个版本包含多个生产者和消费者,使用缓冲channel和waitgroup来实现。
type Task struct{} //自己实际需要的数据结构
producer() //实际生产数据逻辑
consumer() //实际处理逻辑
main()中的consumerNum(消费者个数),
channelLen(通道长度)也可根据实际需要修改
代码实现:
package main
import (
"fmt"
"sync"
)
type Task struct {
Data string
}
var wg sync.WaitGroup
//生产逻辑
func producer(tasks chan Task) {
t := Task{}
for i := 62; i < 72; i++ {
t.Data = string(i)
tasks <- t
}
}
func producerDispatch(tasks chan Task) {
defer close(tasks)
producer(tasks)
}
//消费数据处理逻辑
func consumer(task Task) {
fmt.Printf("consum task:%v\n", task)
}
func consumerDispatch(tasks chan Task) {
defer wg.Done()
for task := range tasks {
consumer(task)
}
}
func main() {
//消费者个数
var consumerNum = 10
var channelLen = 50
tasks := make(chan Task, channelLen)
//当producer执行完成后关闭队列
go producerDispatch(tasks)
for i := 0; i < consumerNum; i++ {
wg.Add(1)
//当consumer消费完后wg.Done()
go consumerDispatch(tasks)
}
//等待全部goroutine完成
wg.Wait()
fmt.Println("all done")
}
这个版本包含多个生产者和消费者,使用无锁队列和多个协程池来实现。
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
type Queue struct {
items []int
head int
tail int
}
func NewQueue(size int) *Queue {
return &Queue{make([]int, size), 0, 0}
}
func (q *Queue) Push(item int) bool {
next := (q.tail + 1) % len(q.items)
if next == q.head {
return false
}
q.items[q.tail] = item
q.tail = next
return true
}
func (q *Queue) Pop() (int, bool) {
if q.head == q.tail {
return 0, false
}
item := q.items[q.head]
q.head = (q.head + 1) % len(q.items)
return item, true
}
type WorkerPool struct {
workers []chan func()
wg sync.WaitGroup
}
func NewWorkerPool(nWorker int) *WorkerPool {
pool := &WorkerPool{}
pool.workers = make([]chan func(), nWorker)
for i := range pool.workers {
pool.workers[i] = make(chan func())
pool.wg.Add(1)
go pool.workerLoop(pool.workers[i])
}
return pool
}
func (p *WorkerPool) workerLoop(worker chan func()) {
defer p.wg.Done()
for task := range worker {
task()
}
}
func (p *WorkerPool) AddTask(task func()) {
worker := p.workers[rand.Intn(len(p.workers))]
worker <- task
}
func (p *WorkerPool) Close() {
for i := range p.workers {
close(p.workers[i])
}
p.wg.Wait()
}
func produce(queue *Queue, pool *WorkerPool, pid int) {
for i := 1; i <= 3; i++ {
success := queue.Push(i * pid)
if !success {
fmt.Printf("生产者%d生产失败\n", pid)
} else {
fmt.Printf("生产者%d生产%d\n", pid, i*pid)
pool.AddTask(func() {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(3000)))
})
}
}
}
func consume(queue *Queue, pool *WorkerPool, cid int) {
for {
item, ok := queue.Pop()
if !ok {
return
}
fmt.Printf("消费者%d消费%d\n", cid, item)
pool.AddTask(func() {
time.Sleep(time.Millisecond * time.Duration(rand.Intn(3000)))
})
}
}
func main() {
queue := NewQueue(5)
pool := NewWorkerPool(5)
nProducer := 3
for i := 1; i <= nProducer; i++ {
go produce(queue, pool, i)
}
nConsumer := 2
for i := 1; i <= nConsumer; i++ {
go consume(queue, pool, i)
}
time.Sleep(time.Second * 10)
pool.Close()
}