Select多路复用
学习目标
知识点 | 掌握程度 | 应用场景 |
---|---|---|
select实现原理 | 深入理解底层机制 | channel通信和多路选择 |
超时处理 | 掌握超时控制方法 | 避免阻塞和资源浪费 |
优先级控制 | 理解优先级实现 | 处理多个channel的顺序 |
性能考虑 | 了解性能优化点 | 高并发场景优化 |
1. Select实现原理
让我们通过一个完整的例子来理解select的工作原理:
go
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
// 数据生产者
type Producer struct {
dataChan chan int
done chan struct{}
}
// 创建新的生产者
func NewProducer() *Producer {
return &Producer{
dataChan: make(chan int, 100),
done: make(chan struct{}),
}
}
// 启动生产
func (p *Producer) Start() {
go func() {
defer close(p.dataChan)
for {
select {
case <-p.done:
fmt.Println("Producer: received stop signal")
return
default:
// 生成随机数据
data := rand.Intn(100)
select {
case p.dataChan <- data:
fmt.Printf("Producer: sent data %d\n", data)
time.Sleep(time.Millisecond * 100)
case <-p.done:
fmt.Println("Producer: received stop signal while sending")
return
}
}
}
}()
}
// 停止生产
func (p *Producer) Stop() {
close(p.done)
}
// 获取数据通道
func (p *Producer) DataChan() <-chan int {
return p.dataChan
}
// 数据处理器
type Processor struct {
producers []*Producer
results chan int
done chan struct{}
}
// 创建新的处理器
func NewProcessor(producerCount int) *Processor {
producers := make([]*Producer, producerCount)
for i := 0; i < producerCount; i++ {
producers[i] = NewProducer()
}
return &Processor{
producers: producers,
results: make(chan int, producerCount*100),
done: make(chan struct{}),
}
}
// 启动处理
func (p *Processor) Start() {
// 启动所有生产者
for i, producer := range p.producers {
producer.Start()
// 为每个生产者启动一个处理goroutine
go func(id int, prod *Producer) {
for {
select {
case data, ok := <-prod.DataChan():
if !ok {
fmt.Printf("Processor %d: producer channel closed\n", id)
return
}
// 处理数据
result := data * 2
select {
case p.results <- result:
fmt.Printf("Processor %d: processed data %d -> %d\n",
id, data, result)
case <-p.done:
return
}
case <-p.done:
fmt.Printf("Processor %d: received stop signal\n", id)
return
}
}
}(i, producer)
}
}
// 停止处理
func (p *Processor) Stop() {
close(p.done)
for _, producer := range p.producers {
producer.Stop()
}
}
// 获取结果通道
func (p *Processor) Results() <-chan int {
return p.results
}
func main() {
// 创建有3个生产者的处理器
processor := NewProcessor(3)
// 启动处理器
processor.Start()
// 创建结果收集器
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
count := 0
for result := range processor.Results() {
fmt.Printf("Collector: received result %d\n", result)
count++
if count >= 20 { // 收集20个结果后停止
processor.Stop()
break
}
}
}()
// 等待处理完成
wg.Wait()
fmt.Println("Main: processing completed")
}
1.1 Select执行流程图
2. 超时处理
让我们实现一个带有超时控制的服务请求处理系统:
go
package main
import (
"context"
"fmt"
"math/rand"
"sync"
"time"
)
// 请求处理器
type RequestHandler struct {
requests chan Request
responses chan Response
done chan struct{}
wg sync.WaitGroup
}
// 请求结构
type Request struct {
ID int
Timeout time.Duration
Data string
}
// 响应结构
type Response struct {
RequestID int
Result string
Error error
}
// 创建新的请求处理器
func NewRequestHandler() *RequestHandler {
return &RequestHandler{
requests: make(chan Request, 100),
responses: make(chan Response, 100),
done: make(chan struct{}),
}
}
// 启动处理器
func (h *RequestHandler) Start(workers int) {
for i := 0; i < workers; i++ {
h.wg.Add(1)
go h.worker(i)
}
}
// 工作协程
func (h *RequestHandler) worker(id int) {
defer h.wg.Done()
for {
select {
case req, ok := <-h.requests:
if !ok {
fmt.Printf("Worker %d: request channel closed\n", id)
return
}
// 创建context用于超时控制
ctx, cancel := context.WithTimeout(context.Background(), req.Timeout)
// 处理请求
response := h.processRequest(ctx, req)
// 发送响应
select {
case h.responses <- response:
fmt.Printf("Worker %d: sent response for request %d\n",
id, req.ID)
case <-h.done:
cancel()
return
}
cancel() // 清理context
case <-h.done:
fmt.Printf("Worker %d: received stop signal\n", id)
return
}
}
}
// 处理单个请求
func (h *RequestHandler) processRequest(ctx context.Context, req Request) Response {
// 模拟处理时间
processTime := time.Duration(rand.Intn(int(req.Timeout))) + req.Timeout/2
select {
case <-time.After(processTime):
return Response{
RequestID: req.ID,
Result: fmt.Sprintf("Processed: %s", req.Data),
}
case <-ctx.Done():
return Response{
RequestID: req.ID,
Error: ctx.Err(),
}
}
}
// 提交请求
func (h *RequestHandler) SubmitRequest(req Request) error {
select {
case h.requests <- req:
return nil
case <-h.done:
return fmt.Errorf("handler is stopped")
}
}
// 获取响应
func (h *RequestHandler) GetResponse() (Response, error) {
select {
case resp := <-h.responses:
return resp, nil
case <-h.done:
return Response{}, fmt.Errorf("handler is stopped")
}
}
// 停止处理器
func (h *RequestHandler) Stop() {
close(h.done)
h.wg.Wait()
close(h.requests)
close(h.responses)
}
func main() {
// 创建请求处理器
handler := NewRequestHandler()
handler.Start(3)
// 发送一些测试请求
requests := []Request{
{ID: 1, Timeout: time.Second, Data: "Fast request"},
{ID: 2, Timeout: time.Second * 2, Data: "Normal request"},
{ID: 3, Timeout: time.Millisecond * 500, Data: "Quick request"},
{ID: 4, Timeout: time.Second * 3, Data: "Slow request"},
}
// 提交请求
for _, req := range requests {
if err := handler.SubmitRequest(req); err != nil {
fmt.Printf("Failed to submit request %d: %v\n", req.ID, err)
continue
}
fmt.Printf("Submitted request %d\n", req.ID)
}
// 收集响应
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < len(requests); i++ {
resp, err := handler.GetResponse()
if err != nil {
fmt.Printf("Failed to get response: %v\n", err)
continue
}
if resp.Error != nil {
fmt.Printf("Request %d failed: %v\n", resp.RequestID, resp.Error)
} else {
fmt.Printf("Request %d succeeded: %s\n", resp.RequestID, resp.Result)
}
}
}()
// 等待所有响应处理完成
wg.Wait()
// 停止处理器
handler.Stop()
fmt.Println("Main: processing completed")
}
3. 优先级控制
让我们实现一个带有优先级控制的任务调度系统:
go
package main
import (
"fmt"
"math/rand"
"sort"
"sync"
"time"
)
// 优先级级别
const (
PriorityHigh = iota
PriorityMedium
PriorityLow
)
// 任务结构
type Task struct {
ID int
Priority int
Action func() error
}
// 优先级调度器
type PriorityScheduler struct {
highPriority chan Task
mediumPriority chan Task
lowPriority chan Task
results chan error
done chan struct{}
wg sync.WaitGroup
}
// 创建新的调度器
func NewPriorityScheduler() *PriorityScheduler {
return &PriorityScheduler{
highPriority: make(chan Task, 100),
mediumPriority: make(chan Task, 100),
lowPriority: make(chan Task, 100),
results: make(chan error, 100),
done: make(chan struct{}),
}
}
// 启动调度器
func (s *PriorityScheduler) Start(workers int) {
for i := 0; i < workers; i++ {
s.wg.Add(1)
go s.worker(i)
}
}
// 工作协程
func (s *PriorityScheduler) worker(id int) {
defer s.wg.Done()
for {
// 使用优先级顺序处理任务
select {
case <-s.done:
return
// 高优先级任务
case task := <-s.highPriority:
fmt.Printf("Worker %d: processing high priority task %d\n",
id, task.ID)
s.results <- task.Action()
// 如果没有高优先级任务,检查中优先级
default:
select {
case <-s.done:
return
case task := <-s.highPriority:
fmt.Printf("Worker %d: processing high priority task %d\n",
id, task.ID)
s.results <- task.Action()
case task := <-s.mediumPriority:
fmt.Printf("Worker %d: processing medium priority task %d\n",
id, task.ID)
s.results <- task.Action()
// 如果没有中优先级任务,检查低优先级
default:
select {
case <-s.done:
return
case task := <-s.highPriority:
fmt.Printf("Worker %d: processing high priority task %d\n",
id, task.ID)
s.results <- task.Action()
case task := <-s.mediumPriority:
fmt.Printf("Worker %d: processing medium priority task %d\n",
id, task.ID)
s.results <- task.Action()
case task := <-s.lowPriority:
fmt.Printf("Worker %d: processing low priority task %d\n",
id, task.ID)
s.results <- task.Action()
}
}
}
}
}
// 提交任务
func (s *PriorityScheduler) SubmitTask(task Task) error {
var targetChan chan Task
switch task.Priority {
case PriorityHigh:
targetChan = s.highPriority
case PriorityMedium:
targetChan = s.mediumPriority
case PriorityLow:
targetChan = s.lowPriority
default:
return fmt.Errorf("invalid priority level: %d", task.Priority)
}
select {
case targetChan <- task:
return nil
case <-s.done:
return fmt.Errorf("scheduler is stopped")
}
}
// 获取结果
func (s *PriorityScheduler) Results() <-chan error {
return s.results
}
// 停止调度器
func (s *PriorityScheduler) Stop() {
close(s.done)
s.wg.Wait()
close(s.highPriority)
close(s.mediumPriority)
close(s.lowPriority)
close(s.results)
}
// 创建模拟任务
func createTask(id int, priority int, duration time.Duration) Task {
return Task{
ID: id,
Priority: priority,
Action: func() error {
time.Sleep(duration)
if rand.Float32() < 0.1 { // 10%的失败率
return fmt.Errorf("task %d failed", id)
}
return nil
},
}
}
func main() {
// 创建调度器
scheduler := NewPriorityScheduler()
scheduler.Start(3)
// 创建一些测试任务
var tasks []Task
for i := 0; i < 15; i++ {
priority := i % 3 // 在三个优先级之间循环
duration := time.Millisecond * time.Duration(rand.Intn(500)+100)
tasks = append(tasks, createTask(i, priority, duration))
}
// 随机打乱任务顺序
rand.Shuffle(len(tasks), func(i, j int) {
tasks[i], tasks[j] = tasks[j], tasks[i]
})
// 提交任务
for _, task := range tasks {
if err := scheduler.SubmitTask(task); err != nil {
fmt.Printf("Failed to submit task %d: %v\n", task.ID, err)
continue
}
fmt.Printf("Submitted task %d with priority %d\n", task.ID, task.Priority)
}
// 收集结果
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
completed := 0
failures := 0
for err := range scheduler.Results() {
if err != nil {
failures++
fmt.Printf("Task failed: %v\n", err)
}
completed++
if completed >= len(tasks) {
break
}
}
fmt.Printf("\nProcessing completed: %d tasks total, %d failures\n",
completed, failures)
}()
// 等待所有任务完成
wg.Wait()
// 停止调度器
scheduler.Stop()
fmt.Println("Main: scheduler stopped")
}
让我们继续完成优先级控制的示例代码:
3.1 优先级控制流程图
4. 性能考虑
4.1 Select性能优化建议
- case数量控制
- select中的case数量会影响性能
- 建议控制在合理范围内(通常不超过5-10个)
- channel缓冲区
- 适当使用带缓冲的channel可以提高性能
- 避免频繁的阻塞和唤醒
- default分支使用
- 合理使用default避免无谓的阻塞
- 考虑轮询间隔,避免CPU空转
让我们实现一个性能优化的示例:
go
package main
import (
"fmt"
"runtime"
"sync"
"sync/atomic"
"time"
)
// 性能统计
type Stats struct {
processed uint64
dropped uint64
blocked uint64
}
// 批处理器
type BatchProcessor struct {
input chan interface{}
output chan []interface{}
done chan struct{}
stats *Stats
batchSize int
maxWait time.Duration
}
// 创建新的批处理器
func NewBatchProcessor(batchSize int, maxWait time.Duration) *BatchProcessor {
return &BatchProcessor{
input: make(chan interface{}, batchSize*2),
output: make(chan []interface{}, batchSize),
done: make(chan struct{}),
stats: &Stats{},
batchSize: batchSize,
maxWait: maxWait,
}
}
// 启动处理
func (p *BatchProcessor) Start(workers int) {
for i := 0; i < workers; i++ {
go p.worker(i)
}
// 启动统计打印
go p.printStats()
}
// 工作协程
func (p *BatchProcessor) worker(id int) {
batch := make([]interface{}, 0, p.batchSize)
timer := time.NewTimer(p.maxWait)
defer timer.Stop()
for {
// 重置计时器
if !timer.Stop() {
select {
case <-timer.C:
default:
}
}
timer.Reset(p.maxWait)
// 优化的批处理逻辑
for len(batch) < p.batchSize {
select {
case <-p.done:
return
case item := <-p.input:
batch = append(batch, item)
atomic.AddUint64(&p.stats.processed, 1)
case <-timer.C:
// 达到最大等待时间,处理当前批次
if len(batch) > 0 {
p.processBatch(batch)
batch = batch[:0]
}
atomic.AddUint64(&p.stats.blocked, 1)
continue
default:
// 如果输入队列为空且已有数据,立即处理
if len(batch) > 0 {
p.processBatch(batch)
batch = batch[:0]
}
// 短暂休眠避免CPU空转
runtime.Gosched()
continue
}
// 批次满了就处理
if len(batch) >= p.batchSize {
p.processBatch(batch)
batch = batch[:0]
}
}
}
}
// 处理批次数据
func (p *BatchProcessor) processBatch(batch []interface{}) {
// 创建副本避免数据竞争
output := make([]interface{}, len(batch))
copy(output, batch)
// 尝试发送处理结果
select {
case p.output <- output:
// 成功发送
default:
// 输出channel满了,增加丢弃计数
atomic.AddUint64(&p.stats.dropped, uint64(len(batch)))
}
}
// 提交数据
func (p *BatchProcessor) Submit(item interface{}) error {
select {
case p.input <- item:
return nil
case <-p.done:
return fmt.Errorf("processor is stopped")
default:
atomic.AddUint64(&p.stats.dropped, 1)
return fmt.Errorf("input channel full")
}
}
// 获取输出通道
func (p *BatchProcessor) Output() <-chan []interface{} {
return p.output
}
// 定期打印统计信息
func (p *BatchProcessor) printStats() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
var lastProcessed, lastDropped, lastBlocked uint64
for {
select {
case <-p.done:
return
case <-ticker.C:
processed := atomic.LoadUint64(&p.stats.processed)
dropped := atomic.LoadUint64(&p.stats.dropped)
blocked := atomic.LoadUint64(&p.stats.blocked)
fmt.Printf("Stats - Processed: %d/s, Dropped: %d/s, Blocked: %d/s\n",
processed-lastProcessed,
dropped-lastDropped,
blocked-lastBlocked)
lastProcessed = processed
lastDropped = dropped
lastBlocked = blocked
}
}
}
// 停止处理器
func (p *BatchProcessor) Stop() {
close(p.done)
}
func main() {
// 创建批处理器
processor := NewBatchProcessor(100, time.Millisecond*50)
processor.Start(3)
// 模拟高速数据提交
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for j := 0; j < 10000; j++ {
data := fmt.Sprintf("Data-%d-%d", id, j)
processor.Submit(data)
time.Sleep(time.Microsecond * time.Duration(50+id*10))
}
}(i)
}
// 处理输出
go func() {
for batch := range processor.Output() {
// 这里可以进行批量处理,比如写入数据库
fmt.Printf("Received batch of size %d\n", len(batch))
}
}()
// 等待提交完成
wg.Wait()
time.Sleep(time.Second) // 等待最后的处理完成
// 停止处理器
processor.Stop()
fmt.Println("Main: processing completed")
}
4.2 性能优化要点
- 避免过度使用select
- 只在必要的地方使用select
- 考虑其他并发控制方式
- channel设计优化
- 合理设置缓冲区大小
- 避免频繁的channel创建和关闭
- goroutine管理
- 控制goroutine数量
- 实现优雅的退出机制
- 内存优化
- 重用切片和对象
- 避免不必要的内存分配
总结
核心要点
- Select实现原理
- 随机选择机制
- 阻塞和非阻塞模式
- 多路复用特性
- 超时处理
- 超时控制方法
- 资源释放保证
- 错误处理机制
- 优先级控制
- 优先级实现方式
- 任务调度策略
- 公平性保证
- 性能优化
- select使用建议
- channel优化
- 资源管理
怎么样今天的内容还满意吗?再次感谢观众老爷的观看,关注GZH:凡人的AI工具箱,回复666,送您价值199的AI大礼包。最后,祝您早日实现财务自由,还请给个赞,谢谢!