goroutine计算自然数的和——拆分task、channel、WaitGroup
功能
计算100个自然数的和。
1 退出通知机制实现
1.1 程序逻辑
- InitTask函数构建task并发送到task通道。
- 分发任务函数DistributeTask为每个task启动goroutine处理任务,并等待其处理完成,然后关闭通道。
- ProcessResult函数读取并统计所有的值。
- 这几个函数在不同的goroutine中运行,它们通过通道和sync.WaitGroup进行通信与同步。
1.2 代码
package main
import (
"fmt"
"sync"
)
//工作任务
type task struct {
begin int
end int
result chan<- int //发送
}
//任务执行,计算begin到end的和,并将结果写入chan result
func (t *task) do() {
sum := 0
for i := t.begin; i <= t.end; i++ {
sum += i
}
t.result <- sum
}
//构建任务并写入task通道
func InitTask(taskchan chan<- task, r chan int, p int) {
qu := p / 10
mod := p % 10
high := qu * 10
for j := 0; j < qu; j++ {
b := 10*j + 1
e := 10 * (j + 1)
tsk := task{
begin: b,
end: e,
result: r,
}
taskchan <- tsk
}
if mod != 0 {
tsk := task{
begin: high + 1,
end: p,
result: r,
}
taskchan <- tsk
}
close(taskchan)
}
//读取task chan,每个task启动一个worker goroutine进行处理,并等待task运行完,关闭通道
func DistributeTask(taskchan <-chan task, wait *sync.WaitGroup, result chan int) {
for v := range taskchan {
wait.Add(1)
go ProcessTask(v, wait)
}
wait.Wait()
close(result)
}
//处理的具体工作
func ProcessTask(t task, wait *sync.WaitGroup) {
t.do()
wait.Done()
}
//读取结果通道,汇总结果
func ProcessResult(resultchan chan int) int {
sum := 0
for r := range resultchan {
sum += r
}
return sum
}
func main() {
//创建任务通道
taskchan := make(chan task, 10)
//创建结果通道
resultchan := make(chan int, 10)
//用于同步等待任务执行
wait := &sync.WaitGroup{}
//初始化task的goroutine 计算100的和
go InitTask(taskchan, resultchan, 100)
//每个task启动一个worker goroutine进行处理
go DistributeTask(taskchan, wait, resultchan)
//获取结果并统计
sum := ProcessResult(resultchan)
fmt.Println(sum)
}
2 线程池和done信号机制
2.1 程序逻辑
- 构建task并发送到task通道中
- 分别启动多个工作线程,不停滴从task中获取任务,将结果写入结果通道,如果任务通道被关闭,则负责向收敛结果的goroutine发送通知,告诉他workers已完成工作
- goroutine接收到task已完成的信号时,主动关闭结果通道
- main中函数进行读取并统计所有的结果
2.2 代码
package main
import (
"fmt"
)
//线程池
const (
NUMBER = 10
)
//工作任务
type task struct {
begin int
end int
result chan<- int //发送
}
//任务执行,计算begin到end的和,并将结果写入chan result
func (t *task) do() {
sum := 0
for i := t.begin; i <= t.end; i++ {
sum += i
}
t.result <- sum
}
//构建任务并写入task通道,结果写入r通道
func InitTaskforPool(taskchan chan<- task, r chan int, p int) {
qu := p / 10
mod := p % 10
high := qu * 10
for j := 0; j < qu; j++ {
b := 10*j + 1
e := 10 * (j + 1)
tsk := task{
begin: b,
end: e,
result: r,
}
taskchan <- tsk
}
if mod != 0 {
tsk := task{
begin: high + 1,
end: p,
result: r,
}
taskchan <- tsk
}
close(taskchan)
}
//读取task chan,每个task启动一个worker goroutine进行处理
func DistributeTaskforPool(taskchan <-chan task, workers int, done chan struct{}) {
for i := 0; i < workers; i++ {
go ProcessTaskforPool(taskchan, done)
}
}
//处理的具体工作,结果写入result通道
func ProcessTaskforPool(taskchan <-chan task, done chan struct{}) {
for t := range taskchan {
t.do()
}
done <- struct{}{}
}
//通过done同步等待所有工作的goroutine结束,关闭结果chan
func CloneResult(done chan struct{}, resultchan chan int, workers int) {
for i := 0; i < workers; i++ {
<-done
}
close(done)
close(resultchan)
}
//读取结果通道,汇总结果
func ProcessResult(resultchan chan int) int {
sum := 0
for r := range resultchan {
sum += r
}
return sum
}
func main() {
//线程池大小
workers := NUMBER
//创建任务通道
taskchan := make(chan task, 10)
//创建结果通道
resultchan := make(chan int, 10)
//信号通道
done := make(chan struct{}, 10)
//初始化任务,taskchan通道写入多个小task,result写入多个小result
go InitTaskforPool(taskchan, resultchan, 100)
//分发任务给多个workers
DistributeTaskforPool(taskchan, workers, done)
//获取停止信号后,关闭结果通道
go CloneResult(done, resultchan, workers)
sum := ProcessResult(resultchan)
fmt.Println(sum)
}