1 开启100个协程,顺序打印1-1000,且保证协程号1的,打印尾数为1的数字
// 同时开启100个协程(分别为1号协程 2号协程 ... 100号协程,
// 1号协程只打印尾数为1的数字,2号协程只打印尾数为2的数,
// 以此类推),请顺序打印1-1000整数以及对应的协程号;
func goCode01() {
s := make(chan struct{})
var wg sync.WaitGroup
m := make(map[int]chan int, 100)
for i := 1; i <= 100; i++ {
m[i] = make(chan int)
}
for i := 1; i <= 100; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for {
num, ok := <-m[i]
// ok标记:false代表通道关闭
if !ok {
fmt.Printf("子协程 %d 号: 通道关闭 , exit\n", i)
break // 通道关闭,退出循环,goroutine结束
}
fmt.Printf("子协程 %d 打印: %d\n", i, num)
// 收到数字,通知主协程
s <- struct{}{}
}
}(i)
}
// 投递任务 1~1000
for i := 1; i <= 1000; i++ {
id := i % 100
if id == 0 {
id = 100
}
m[id] <- i // 往通道传递数据,然后阻塞
<-s //子协程收到数据了,通知主协程
}
// 任务完成,关闭所有子协程通道
for _, ch := range m {
close(ch)
}
// 等待所有worker退出,替代sleep
wg.Wait()
fmt.Println("main exit")
}
2 三个goroutinue交替打印abc 10次
func goCode02() {
cha := make(chan struct{})
chb := make(chan struct{})
chc := make(chan struct{})
var wg sync.WaitGroup
times := 10
wg.Add(3)
go func() {
defer wg.Done()
for i := 1; i <= times; i++ {
<-cha
fmt.Printf("协程1打印第%d个a\n", i)
// 通知协程2打印
chb <- struct{}{}
}
// 再专门接收 C 最后多出来的这个信号。不然协程3会阻塞
<-cha
}()
go func() {
defer wg.Done()
for i := 1; i <= times; i++ {
<-chb
fmt.Printf("协程2打印第%d个b\n", i)
// 通知协程3打印
chc <- struct{}{}
}
}()
go func() {
defer wg.Done()
for i := 1; i <= times; i++ {
<-chc
fmt.Printf("协程3打印第%d个c\n", i)
// 通知协程1打印
cha <- struct{}{}
}
}()
// 启动:先给A发第一个信号,开始第一轮
cha <- struct{}{}
wg.Wait()
fmt.Println("全部打印完成")
}
3 用不超过10个goroutine不重复的打印slice中的100个元素
func goCode03() {
var wg sync.WaitGroup
// 构造100个元素
data := make([]int, 100)
for index := range data {
data[index] = index
}
// 创建带缓存通道,通道缓存20个数据
s := make(chan int, 20)
workNum := 10
for i := 1; i <= workNum; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for {
// 接收数据
num, ok := <-s
if !ok {
fmt.Printf("子协程%d 通道关闭 \n", i)
break
}
fmt.Printf("子协程%d 打印%d \n", i, num)
}
}(i)
}
go func() {
// 往通道发数据
for _, v := range data {
s <- v
}
// 发完数据,通道关闭
close(s)
}()
wg.Wait()
println("主协程关闭")
}
4 两个协程交替打印奇偶数
func goCode04() {
chOdd := make(chan struct{}) // 奇数通道
chEven := make(chan struct{}) // 偶数通道
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i <= 100; i++ {
<-chOdd
if i%2 == 1 {
fmt.Printf("奇数:%d\n", i)
}
chEven <- struct{}{}
}
<-chOdd
}()
go func() {
defer wg.Done()
for i := 0; i <= 100; i++ {
<-chEven
if i%2 == 0 {
fmt.Printf("偶数:%d\n", i)
}
chOdd <- struct{}{}
}
}()
// 启动,先唤醒打印奇数的协程
chOdd <- struct{}{}
wg.Wait()
fmt.Println("打印完成")
}
5 用单个channel实现0,1的交替打印
func goCode05() {
s := make(chan struct{})
go func() {
for {
<-s
fmt.Printf("协程a打印0\n")
s <- struct{}{}
}
}()
go func() {
for {
<-s
fmt.Printf("协程b打印1\n")
s <- struct{}{}
}
}()
s <- struct{}{}
time.Sleep(3 * time.Second)
}
6 sync.Cond实现多生产者多消费者
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
var cond sync.Cond
cond.L = new(sync.Mutex)
msgCh := make(chan int, 5)
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
rand.Seed(time.Now().UnixNano())
// 生产者
producer := func(ctx context.Context, out chan<- int, idx int) {
defer wg.Done()
for {
select {
case <-ctx.Done():
// 每次生产者退出,都唤醒一个消费者处理,防止最后有消费者线程死锁
// 生产者比消费者多,所以cond.Signal()就可以。不然的话建议Broadcast()
cond.Broadcast()
fmt.Println("producer finished")
return
default:
cond.L.Lock()
for len(msgCh) == 5 {
cond.Wait()
}
num := rand.Intn(500)
out <- num
fmt.Printf("producer: %d, msg: %d\n", idx, num)
cond.Signal()
cond.L.Unlock()
}
}
}
// 消费者
consumer := func(ctx context.Context, in <-chan int, idx int) {
defer wg.Done()
for {
select {
case <-ctx.Done():
// 消费者可以选择继续消费直到channel为空
for len(msgCh) > 0 {
select {
case num := <-in:
fmt.Printf("consumer %d, msg: %d\n", idx, num)
default:
// 如果channel已经空了,跳出循环
break
}
}
fmt.Println("consumer finished")
return
default:
cond.L.Lock()
for len(msgCh) == 0 {
cond.Wait()
}
num := <-in
fmt.Printf("consumer %d, msg: %d\n", idx, num)
cond.Signal()
cond.L.Unlock()
}
}
}
// 启动生产者和消费者
for i := 0; i < 5; i++ {
wg.Add(1)
go producer(ctx, msgCh, i+1)
}
for i := 0; i < 3; i++ {
wg.Add(1)
go consumer(ctx, msgCh, i+1)
}
// 模拟程序运行一段时间
wg.Wait()
close(msgCh)
fmt.Println("all finished")
}
7 使用go实现1000个并发控制并设置执行超时时间1秒
func goCode06() {
const (
totalTask = 1000 // 总任务数量
concurrency = 100 // 最大并发数,即控制同时跑多少goroutine
globalTimeout = 1 * time.Second // 1秒
)
ctx, cancle := context.WithTimeout(context.Background(), globalTimeout)
defer cancle()
taskCh := make(chan int, totalTask)
pool := make(chan struct{}, concurrency) // 并发控制令牌池
var wg sync.WaitGroup
for i := 1; i <= totalTask; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
for {
select {
case <-ctx.Done():
// 超时/取消,worker退出
fmt.Println("worker 收到超时信号退出")
return
case id, ok := <-taskCh:
if !ok {
// 任务通道关闭,没有任务了,退出
return
}
// 获取令牌,占用一个并发名额 pool 有100个控制并发
pool <- struct{}{}
fmt.Printf("协程%d获取令牌\n", i)
goCode06DoTask(id)
<-pool
}
}
}(i)
}
// 生产者:往通道投递任务
producer:
for i := 0; i <= totalTask; i++ {
select {
case <-ctx.Done():
fmt.Println("任务投递超时,停止生产任务")
break producer
case taskCh <- i:
}
}
close(taskCh) // 关闭任务通道,任务发配完成。
wg.Wait()
if ctx.Err() != nil {
fmt.Printf("任务结束,原因:%v\n", ctx.Err())
} else {
fmt.Println("所有任务全部正常执行完成")
}
}
func goCode06DoTask(id int) {
time.Sleep(10 * time.Millisecond)
fmt.Printf("协程执行任务:%d\n", id)
}
8 使用两个Goroutine,向标准输出中按顺序按顺序交替打出字母与数字,输出是a1b2c3
func goCode07() {
chLetter := make(chan struct{})
chNum := make(chan struct{})
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for i := 'a'; i <= 'z'; i++ {
<-chLetter
fmt.Printf("%s\n", string(i))
chNum <- struct{}{}
}
<-chLetter
}()
go func() {
defer wg.Done()
for i := 1; i <= 26; i++ {
<-chNum
fmt.Printf("%d\n", i)
chLetter <- struct{}{}
}
}()
chLetter <- struct{}{}
wg.Wait()
}
9 编写一个程序限制10个goroutine执行,每执行完一个goroutine就放一个新的goroutine进来
func goCode08() {
const maxConcurrency = 10 // 限制最多10个并发
const totalTask = 20 // 总共有20个任务
token := make(chan struct{}, maxConcurrency) //令牌池,容量10
var wg sync.WaitGroup
for i := 0; i < totalTask; i++ {
token <- struct{}{} // 获取令牌,满了这里就阻塞,控制最多10个并发
wg.Add(1)
go func(taskId int) {
defer func() {
<-token // 任务执行完毕,释放令牌!释放之后,外面循环才能继续进下一个
wg.Done()
}()
// 模拟业务执行
fmt.Printf("任务 %d 开始执行\n", taskId)
time.Sleep(1 * time.Second)
fmt.Printf("任务 %d 执行完成\n", taskId)
}(i)
}
wg.Wait()
close(token)
fmt.Println("所有任务执行完毕")
}