面试题答案
一键面试策略和技术手段
- 使用defer语句处理资源释放:在函数中使用
defer
关键字,在函数结束时自动执行一些清理操作,如关闭文件、释放数据库连接等。这样即使函数在执行过程中发生错误提前返回,资源也能得到正确释放。 - 互斥锁(sync.Mutex)处理数据竞争:当多个goroutine可能同时访问和修改共享资源时,使用互斥锁来确保同一时间只有一个goroutine能够访问共享资源,从而避免数据竞争。
- 读写锁(sync.RWMutex)优化读多写少场景:如果共享资源的读取操作远远多于写入操作,可以使用读写锁。多个goroutine可以同时进行读操作,但写操作时需要独占锁,以保证数据一致性。
- 使用context处理取消和超时:
context
包提供了一种机制来管理goroutine的生命周期,包括取消操作和设置超时。在高并发I/O操作或数据库访问中,通过context
可以优雅地取消未完成的操作,避免资源浪费。 - 错误返回和检查:函数应该及时返回错误,并在调用处进行检查。不要忽略错误,应根据具体业务逻辑进行适当处理,如重试、记录日志或返回合适的错误信息给用户。
代码示例
以下是一个综合示例,展示如何在高并发的Go程序中进行优雅的错误处理:
package main
import (
"context"
"fmt"
"io/ioutil"
"log"
"sync"
"time"
)
func readFile(ctx context.Context, filePath string, wg *sync.WaitGroup) {
defer wg.Done()
file, err := ioutil.ReadFile(filePath)
if err != nil {
log.Printf("Error reading file %s: %v", filePath, err)
return
}
select {
case <-ctx.Done():
log.Printf("Operation cancelled for file %s", filePath)
return
default:
fmt.Printf("Content of %s: %s\n", filePath, file)
}
}
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
filePaths := []string{"file1.txt", "file2.txt", "file3.txt"}
for _, filePath := range filePaths {
wg.Add(1)
go readFile(ctx, filePath, &wg)
}
wg.Wait()
}
在上述示例中:
- 使用
defer wg.Done()
确保goroutine结束时正确通知等待组。 - 使用
ioutil.ReadFile
读取文件,并在发生错误时记录日志。 - 通过
context.WithTimeout
设置操作的超时时间,并在select
语句中检查ctx.Done()
来处理取消操作。
对于数据库连接的示例:
package main
import (
"context"
"database/sql"
"fmt"
"log"
"sync"
"time"
_ "github.com/go-sql-driver/mysql"
)
func queryDB(ctx context.Context, db *sql.DB, query string, wg *sync.WaitGroup) {
defer wg.Done()
rows, err := db.QueryContext(ctx, query)
if err != nil {
log.Printf("Error querying database: %v", err)
return
}
defer rows.Close()
for rows.Next() {
var result string
err := rows.Scan(&result)
if err != nil {
log.Printf("Error scanning result: %v", err)
return
}
fmt.Println(result)
}
if err := rows.Err(); err != nil {
log.Printf("Error iterating over rows: %v", err)
}
}
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/test")
if err != nil {
log.Fatalf("Error opening database: %v", err)
}
defer db.Close()
queries := []string{"SELECT * FROM table1", "SELECT * FROM table2"}
for _, query := range queries {
wg.Add(1)
go queryDB(ctx, db, query, &wg)
}
wg.Wait()
}
在这个数据库操作示例中:
- 使用
defer rows.Close()
确保查询结果集在函数结束时关闭。 - 通过
db.QueryContext
执行带上下文的查询,以便在超时或取消时能够正确处理。 - 使用
rows.Scan
处理查询结果,并在发生错误时记录日志。