架构设计
- 协程部分:每个可能出错的协程将错误通过通道发送出去。
- 错误处理部分:有一个专门的协程用于接收这些错误通道传来的错误信息,并进行集中处理。
示例代码(以Go语言为例)
package main
import (
"fmt"
)
// 模拟一个可能出错的任务
func task(id int, resultChan chan int, errorChan chan error) {
if id == 2 { // 假设id为2时出错
errorChan <- fmt.Errorf("task %d has an error", id)
return
}
resultChan <- id * 2
}
func main() {
resultChan := make(chan int)
errorChan := make(chan error)
// 启动多个协程
for i := 1; i <= 3; i++ {
go task(i, resultChan, errorChan)
}
// 错误处理协程
go func() {
for err := range errorChan {
fmt.Println("Error:", err)
}
}()
// 结果处理
for i := 1; i <= 3; i++ {
select {
case result := <-resultChan:
fmt.Println("Result:", result)
case err := <-errorChan:
fmt.Println("Error:", err)
}
}
close(resultChan)
close(errorChan)
}
保证数据一致性和避免死锁
- 数据一致性:
- 使用通道进行数据传递,通道本身是线程安全的,保证了数据在不同协程间传递时的一致性。
- 在示例中,
resultChan
和errorChan
分别传递结果和错误,不会造成数据竞争。
- 避免死锁:
- 确保所有的通道都有对应的发送和接收操作。在示例中,每个协程都会向
resultChan
或errorChan
发送数据,而主协程和错误处理协程会接收这些数据。
- 合理使用
select
语句,在多个通道操作中进行非阻塞的选择,防止协程因等待通道操作而无限期阻塞。例如,在主协程中使用select
语句同时监听resultChan
和errorChan
,避免死锁。
- 正确关闭通道。在示例中,处理完所有任务后关闭
resultChan
和errorChan
,防止因通道未关闭导致的死锁。