方案设计与实现
package main
import (
"fmt"
"sync"
)
// 定义任务结构体
type Task struct {
Data []int
}
// 递归处理任务的函数
func processTask(task Task, wg *sync.WaitGroup, mutex *sync.Mutex) {
defer wg.Done()
// 处理任务数据
for _, num := range task.Data {
// 这里模拟实际的数据处理操作
mutex.Lock()
fmt.Printf("Processing number: %d\n", num)
mutex.Unlock()
}
// 模拟递归任务
if len(task.Data) > 1 {
mid := len(task.Data) / 2
subTask1 := Task{Data: task.Data[:mid]}
subTask2 := Task{Data: task.Data[mid:]}
var subWg sync.WaitGroup
subWg.Add(2)
go processTask(subTask1, &subWg, mutex)
go processTask(subTask2, &subWg, mutex)
subWg.Wait()
}
}
func main() {
tasks := []Task{
{Data: []int{1, 2, 3, 4}},
{Data: []int{5, 6, 7, 8}},
}
var wg sync.WaitGroup
wg.Add(len(tasks))
var mutex sync.Mutex
for _, task := range tasks {
go processTask(task, &wg, &mutex)
}
wg.Wait()
}
资源竞争处理
- 使用互斥锁(Mutex):在共享资源(如打印操作)访问处使用
sync.Mutex
。在上述代码中,在打印数据时,通过 mutex.Lock()
和 mutex.Unlock()
来确保同一时间只有一个协程能访问打印操作,防止资源竞争。
- 读写锁(RWMutex):如果存在大量读操作和少量写操作的场景,可以使用
sync.RWMutex
。读操作时可以允许多个协程同时进行,写操作时则独占资源,以确保数据一致性。
死锁处理
- 合理的锁顺序:确保所有协程以相同的顺序获取锁。如果在代码中有多个锁,按照固定的顺序获取锁可以避免死锁。例如,如果有锁A和锁B,所有协程都先获取锁A,再获取锁B。
- 避免嵌套锁:尽量减少锁的嵌套使用。嵌套锁容易导致死锁,尤其是在复杂的逻辑中。如果必须使用嵌套锁,要仔细规划锁的获取和释放顺序。
- 使用超时机制:在获取锁时设置超时。Go语言的
context
包可以用于实现超时机制。如果在规定时间内没有获取到锁,可以放弃操作并进行相应处理,避免无限等待。例如:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
case <-ctx.Done():
// 处理获取锁超时的情况
fmt.Println("Timeout while acquiring lock")
default:
// 获取锁的操作
mutex.Lock()
defer mutex.Unlock()
// 执行操作
}