【GO】goroutine 协程练习题

基础 goroutine 启动

题目描述

启动一个协程打印数字

  • 定义函数 printNum(),循环打印 1~5
  • main 中使用 go printNum() 启动协程
  • 主协程等待一段时间,让子协程执行完毕

输出示例

主协程等待...

协程打印: 1

协程打印: 2

协程打印: 3

协程打印: 4

协程打印: 5

主协程结束

实现代码

Go 复制代码
package main

import (
	"fmt"
	"time"
)

// 普通函数
func printNum() {
	for i := 1; i <= 5; i++ {
		fmt.Println("协程打印:", i)
		time.Sleep(200 * time.Millisecond)
	}
}

func main() {
	// 启动 goroutine
	go printNum()

	// 主协程等待
	fmt.Println("主协程等待...")
	time.Sleep(1 * time.Second)
	fmt.Println("主协程结束")
}

多个 goroutine 同时运行

题目描述

启动两个协程并发执行

  • 定义函数 task1() 打印 A~C
  • 定义函数 task2() 打印 1~3
  • 同时启动两个协程
  • 观察并发执行效果

输出示例

协程1: A

协程2: 1

协程1: B

协程2: 2

协程1: C

协程2: 3

程序结束

实现代码

Go 复制代码
package main

import (
	"fmt"
	"time"
)

func task1() {
	for i := 'A'; i <= 'C'; i++ {
		fmt.Printf("协程1:%c\n", i)
		time.Sleep(200 * time.Millisecond)
	}
}

func task2() {
	for i := 1; i <= 3; i++ {
		fmt.Println("协程2:", i)
		time.Sleep(200 * time.Millisecond)
	}
}

func main() {
	go task1()
	go task2()

	// 等待协程执行完
	time.Sleep(1 * time.Second)
	fmt.Println("程序结束")
}

三个打印机同时打印

题目描述

办公室有3台打印机,同时打印不同的文件。每台打印机打印5页,每页需要0.3秒。

要求:

  • 创建3个goroutine模拟3台打印机

  • 每台打印机打印5页,每打印一页输出:打印机X 打印了第Y页

  • 使用 time.Sleep 等待所有打印机完成

  • 最后输出:所有文件打印完成

输出示例

打印机2 开始工作...

打印机1 开始工作...

打印机3 开始工作...

打印机1 打印了第1页

打印机2 打印了第1页

打印机3 打印了第1页

打印机1 打印了第2页

打印机2 打印了第2页

打印机3 打印了第2页

打印机1 打印了第3页

打印机2 打印了第3页

打印机3 打印了第3页

打印机1 打印了第4页

打印机2 打印了第4页

打印机3 打印了第4页

打印机1 打印了第5页

打印机1 打印完成!

打印机2 打印了第5页

打印机2 打印完成!

打印机3 打印了第5页

打印机3 打印完成!

所有文件打印完成

实现代码

Go 复制代码
package main

import (
	"fmt"
	"time"
)

func printFile(printerID int, pages int) {
	fmt.Printf("打印机%d 开始工作...\n", printerID)
	
	for page := 1; page <= pages; page++ {
		time.Sleep(300 * time.Millisecond)
		fmt.Printf("打印机%d 打印了第%d页\n", printerID, page)
	}
	
	fmt.Printf("打印机%d 打印完成!\n", printerID)
}

func main() {
	// 启动3台打印机
	go printFile(1, 5)
	go printFile(2, 5)
	go printFile(3, 5)
	
	// 等待所有打印机完成(5页 × 0.3秒 = 1.5秒)
	time.Sleep(2 * time.Second)
	
	fmt.Println("所有文件打印完成")
}

goroutine + channel 通道传值

题目描述

协程通过通道发送数据

  • 创建通道 ch
  • 启动协程,向通道发送数字 100
  • 主协程从通道接收数据并打印

输出示例

协程发送数据:100

主协程接收: 100

实现代码

Go 复制代码
package main

import "fmt"

func sendData(ch chan int) {
	fmt.Println("协程发送数据:100")
	ch <- 100 // 发送到通道
}

func main() {
	// 创建通道
	ch := make(chan int)

	// 启动协程
	go sendData(ch)

	// 主协程接收
	data := <-ch
	fmt.Println("主协程接收:", data)
}

并发计算求和(多协程分摊计算任务)

题目描述

多 goroutine 并发计算 1~100 的总和

  • 启动 2 个协程
  • 协程 1 计算 1~50 的和
  • 协程 2 计算 51~100 的和
  • 通过 channel 将结果传回主协程
  • 主协程汇总输出最终总和

输出示例

1~50 的和: 1275

51~100 的和: 3775

总和: 5050

实现代码

Go 复制代码
package main

import "fmt"

// 计算从 start 到 end 的和,通过 ch 发送结果
func sum(start, end int, ch chan int) {
	total := 0
	for i := start; i <= end; i++ {
		total += i
	}
	ch <- total
}

func main() {
	ch := make(chan int)

	// 启动两个协程分摊任务
	go sum(1, 50, ch)
	go sum(51, 100, ch)

	// 接收两个结果
	s1 := <-ch
	s2 := <-ch

	fmt.Println("1~50 的和:", s1)
	fmt.Println("51~100 的和:", s2)
	fmt.Println("总和:", s1+s2)
}

最大值查找器

题目描述

有一个包含12个数字的数组,创建3个goroutine,每个负责查找一部分数字中的最大值,通过channel返回,主程序找出全局最大值。

要求:

  • 数组:[23, 56, 12, 89, 45, 67, 34, 91, 28, 73, 19, 88]

  • 分成3组:第1组索引0-3,第2组索引4-7,第3组索引8-11

  • 每个goroutine找出自己组内的最大值

  • 使用channel传递结果

  • 主程序找出全局最大值并打印

输出示例

第1组 [23 56 12 89] 最大值: 89

第2组 [45 67 34 91] 最大值: 91

第3组 [28 73 19 88] 最大值: 88

全局最大值: 91

实现代码

Go 复制代码
package main

import (
	"fmt"
)

func findMax(nums []int, groupID int, ch chan int) {
	max := nums[0]
	for _, num := range nums {
		if num > max {
			max = num
		}
	}
	fmt.Printf("第%d组 %v 最大值: %d\n", groupID, nums, max)
	ch <- max
}

func main() {
	ch := make(chan int)
	
	numbers := []int{23, 56, 12, 89, 45, 67, 34, 91, 28, 73, 19, 88}
	
	// 分成3组
	group1 := numbers[0:4]   // [23 56 12 89]
	group2 := numbers[4:8]   // [45 67 34 91]
	group3 := numbers[8:12]  // [28 73 19 88]
	
	// 启动3个goroutine
	go findMax(group1, 1, ch)
	go findMax(group2, 2, ch)
	go findMax(group3, 3, ch)
	
	// 接收3个结果,找出全局最大值
	globalMax := 0
	for i := 0; i < 3; i++ {
		max := <-ch
		if max > globalMax {
			globalMax = max
		}
	}
	
	fmt.Printf("全局最大值: %d\n", globalMax)
}

简单的生产者消费者

题目描述

一个生产者生产数字,一个消费者消费数字。生产者每1秒生产一个数字(1-5),消费者每2秒消费一个数字。

要求:

  • 使用无缓冲channel

  • 生产者发送5个数字

  • 消费者接收并打印

输出示例

生产者: 生产 1

消费者: 消费 1

生产者: 生产 2

消费者: 消费 2

生产者: 生产 3

消费者: 消费 3

生产者: 生产 4

消费者: 消费 4

生产者: 生产 5

消费者: 消费 5

生产消费完成

实现代码

Go 复制代码
package main

import (
	"fmt"
	"time"
)

func producer(ch chan int) {
	for i := 1; i <= 5; i++ {
		time.Sleep(1 * time.Second)
		fmt.Printf("生产者: 生产 %d\n", i)
		ch <- i
	}
	close(ch)
}

func consumer(ch chan int) {
	for num := range ch {
		time.Sleep(2 * time.Second)
		fmt.Printf("消费者: 消费 %d\n", num)
	}
}

func main() {
	ch := make(chan int)
	
	go producer(ch)
	consumer(ch)
	
	fmt.Println("生产消费完成")
}

sync.WaitGroup 等待所有协程完成

题目描述

使用 WaitGroup 等待多个协程

  • 使用 sync.WaitGroup 等待 2 个协程
  • 每个协程执行完调用 Done()
  • 主协程调用 Wait() 阻塞等待

输出示例

协程 1 正在运行

协程 2 正在运行

所有协程执行完毕

实现代码

Go 复制代码
package main

import (
	"fmt"
	"sync"
)

func worker(id int, wg *sync.WaitGroup) {
	// 函数结束时通知完成
	defer wg.Done()

	fmt.Printf("协程 %d 正在运行\n", id)
}

func main() {
	// 创建等待组
	var wg sync.WaitGroup

	// 等待 2 个协程
	wg.Add(2)

	// 启动协程
	go worker(1, &wg)
	go worker(2, &wg)

	// 阻塞等待所有协程完成
	wg.Wait()

	fmt.Println("所有协程执行完毕")
}

生产者 --- 消费者模型

题目描述

goroutine + channel 实现生产者消费者

  • 生产者协程:产生 1~5 数字,发送到通道
  • 消费者协程:从通道读取并处理数据
  • 通道关闭后消费者退出
  • 使用 WaitGroup 等待完成

输出示例

处理任务:1

处理任务:2

处理任务:3

处理任务:4

处理任务:5

所有任务处理完成

实现代码

Go 复制代码
package main

import (
	"fmt"
	"sync"
)

// 生产者:发送数据
func producer(ch chan<- int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 1; i <= 5; i++ {
		ch <- i
	}
	close(ch) // 发送完关闭通道
}

// 消费者:接收处理
func consumer(ch <-chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for num := range ch {
		fmt.Printf("处理任务:%d\n", num)
	}
}

func main() {
	ch := make(chan int)
	var wg sync.WaitGroup

	wg.Add(2)
	go producer(ch, &wg)
	go consumer(ch, &wg)

	wg.Wait()
	fmt.Println("所有任务处理完成")
}

模拟并发下载文件

题目描述

使用 goroutine 模拟并发下载多个文件

  • 定义函数 download (file string),模拟下载耗时
  • 同时下载 3 个文件,用 WaitGroup 等待全部完成
  • 输出每个文件开始、完成信息

输出示例

开始下载:a.jpg

开始下载:b.mp4

开始下载:c.zip

下载完成:a.jpg

下载完成:b.mp4

下载完成:c.zip

所有文件下载完成

实现代码

Go 复制代码
package main

import (
	"fmt"
	"sync"
	"time"
)

func download(file string, wg *sync.WaitGroup) {
	defer wg.Done()

	fmt.Printf("开始下载:%s\n", file)
	time.Sleep(1 * time.Second) // 模拟下载耗时
	fmt.Printf("下载完成:%s\n", file)
}

func main() {
	var wg sync.WaitGroup
	files := []string{"a.jpg", "b.mp4", "c.zip"}

	wg.Add(len(files))
	for _, f := range files {
		go download(f, &wg)
	}

	wg.Wait()
	fmt.Println("所有文件下载完成")
}

多路等待多个任务

题目描述

  • 启动 2 个协程,分别向 ch1ch2 发送字符串
  • 使用 select 同时监听两个通道
  • 打印先到达的消息

输出示例

来自任务2的消息

实现代码

Go 复制代码
package main

import (
	"fmt"
	"time"
)

func send1(ch chan string) {
	time.Sleep(200 * time.Millisecond)
	ch <- "来自任务1的消息"
}

func send2(ch chan string) {
	time.Sleep(100 * time.Millisecond)
	ch <- "来自任务2的消息"
}

func main() {
	ch1 := make(chan string)
	ch2 := make(chan string)

	go send1(ch1)
	go send2(ch2)

	// 多路复用:谁先完成处理谁
	select {
	case msg1 := <-ch1:
		fmt.Println(msg1)
	case msg2 := <-ch2:
		fmt.Println(msg2)
	}
}

多商家接单

题目描述

用户下单后,系统同时向 麦当劳肯德基华莱士 三个商家发送订单。每个商家处理时间不同(1-3秒随机),哪个商家先回复就采用哪个,其他商家的请求自动取消。

  1. 用3个 goroutine 模拟三个商家,各自随机延迟1-3秒后回复

  2. select 同时等待三个商家的回复

  3. 只处理第一个回复的商家,打印"XXX接单成功"

输出示例

等待商家接单...

肯德基接单成功

实现代码

Go 复制代码
package main

import (
	"fmt"
	"math/rand"
	"time"
)

func main() {
	// 创建三个商家通道
	mcd := make(chan string)
	kfc := make(chan string)
	hl := make(chan string)

	// 麦当劳
	go func() {
		time.Sleep(time.Duration(1+rand.Intn(3)) * time.Second)
		mcd <- "麦当劳"
	}()

	// 肯德基
	go func() {
		time.Sleep(time.Duration(1+rand.Intn(3)) * time.Second)
		kfc <- "肯德基"
	}()

	// 华莱士
	go func() {
		time.Sleep(time.Duration(1+rand.Intn(3)) * time.Second)
		hl <- "华莱士"
	}()

	fmt.Println("等待商家接单...")

	// 哪个先来就选哪个
	select {
	case name := <-mcd:
		fmt.Printf("%s接单成功\n", name)
	case name := <-kfc:
		fmt.Printf("%s接单成功\n", name)
	case name := <-hl:
		fmt.Printf("%s接单成功\n", name)
	}
}
相关推荐
北漂Zachary20 小时前
四大编程语言终极对决:汇编/C#/Go/Java谁更强
汇编·golang·c#
Stark-C21 小时前
NAS音乐必备神器,全平台音乐收割机!极空间部署『Go Music DL』
开发语言·后端·golang
ALex_zry1 天前
go-zero Redis缓存封装与Model层设计
redis·缓存·golang·气象
XMYX-02 天前
17 - Go 通道 Channel 底层原理 + 实战详解
开发语言·golang
Tomhex2 天前
Go调用C代码的场景与实践
golang
黑牛儿2 天前
Swoole协程 vs Go协程:PHP开发者一看就懂的实战对比
后端·golang·php·swoole
Wenweno0o2 天前
Eino-Document 组件使用指南
golang·大模型·智能体·eino
lolo大魔王3 天前
Go语言的反射机制
开发语言·后端·算法·golang
XMYX-03 天前
16 - Go 协程(goroutine):从基础到实战
开发语言·golang