实现思路
- 创建一个带有超时的
context.Context
。
- 在启动每个并发任务时,将
context.Context
传递进去。
- 在每个任务内部,通过
context.Context
监听取消信号,一旦接收到取消信号,立即停止任务。
- 确保在程序结束时,正确关闭数据库连接。
关键代码片段
package main
import (
"context"
"database/sql"
"fmt"
"net/http"
"time"
_ "github.com/go-sql-driver/mysql"
)
func main() {
// 假设这里已经正确初始化了数据库连接
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
if err != nil {
panic(err)
}
defer db.Close()
// 创建带有超时的context
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
var numTasks = 3
var taskDone = make(chan struct{}, numTasks)
for i := 0; i < numTasks; i++ {
go func(ctx context.Context, taskID int) {
defer func() { taskDone <- struct{}{} }()
select {
case <-ctx.Done():
fmt.Printf("Task %d cancelled due to context cancellation\n", taskID)
return
default:
// 模拟数据库操作
_, err := db.ExecContext(ctx, "SELECT 1")
if err != nil {
if ctx.Err() == context.Canceled || ctx.Err() == context.DeadlineExceeded {
fmt.Printf("Task %d cancelled due to context: %v\n", taskID, err)
} else {
fmt.Printf("Task %d database operation error: %v\n", taskID, err)
}
return
}
fmt.Printf("Task %d completed successfully\n", taskID)
}
}(ctx, i)
}
for i := 0; i < numTasks; i++ {
<-taskDone
}
close(taskDone)
}
- 创建数据库连接:使用
sql.Open
打开数据库连接,并通过defer db.Close()
确保程序结束时关闭连接。
- 创建带有超时的context:使用
context.WithTimeout
创建一个带有5秒超时的context.Context
,同时获取取消函数cancel
。
- 并发任务:启动多个并发任务,每个任务接收
context.Context
。在任务内部,通过select
语句监听ctx.Done()
信号。如果接收到取消信号,任务立即停止。这里模拟了一个数据库操作db.ExecContext
,如果操作过程中context.Context
取消,数据库操作也会被取消。
- 等待任务完成:使用一个带缓冲的通道
taskDone
来等待所有任务完成。