【Go语言快速上手】第二部分:Go语言进阶
文章目录
- 并发编程
- goroutine:创建和调度 goroutine
- channel:无缓冲 channel、有缓冲 channel、select 语句
- 无缓冲 channel
- 有缓冲 channel
- select 语句
- sync 包:Mutex、RWMutex、WaitGroup 等同步原语
- Mutex:互斥锁
- RWMutex:读写互斥锁
- WaitGroup:等待多个 goroutine 完成
- 网络编程
- TCP/UDP 编程:`net` 包的使用
- TCP 编程
- TCP 服务器
- TCP 客户端
- UDP 编程
- UDP 服务器
- UDP 客户端
- HTTP 编程:`net/http` 包的使用,编写 HTTP 服务器和客户端
- HTTP 服务器
- HTTP 客户端
- WebSocket 编程:`gorilla/websocket` 包的使用
- WebSocket 服务器
- WebSocket 客户端
- 数据库操作
- 使用 `database/sql` 包操作关系型数据库
- 连接数据库
- 安装 MySQL 驱动
- 连接到 MySQL 数据库
- 执行 SQL 查询
- 查询单行数据
- 查询多行数据
- 执行 SQL 插入、更新和删除
- 插入数据
- 更新数据
- 删除数据
- 使用 NoSQL 数据库
- 使用 MongoDB
- 安装 MongoDB 驱动
- 连接到 MongoDB 并执行查询
- 使用 Redis
- 安装 Redis 驱动
- 连接到 Redis 并执行操作
- 测试和性能优化
- 编写单元测试和基准测试
- 单元测试
- 示例:编写单元测试
- 基准测试
- 示例:编写基准测试
- 使用 pprof 进行性能分析
- 启用 pprof
- 示例:启用 pprof
- 使用 pprof 工具分析性能
- 示例:生成 CPU 性能报告
- 示例:生成内存使用报告
- 分析报告
- 代码优化技巧
- 减少内存分配
- 示例:重用切片
- 避免锁竞争
- 示例:避免过多锁的使用
- 使用更高效的算法和数据结构
- 示例:使用哈希表代替线性查找
- 避免重复计算
- 示例:缓存计算结果
- 工具和框架
- 常用工具
- `go fmt`
- 示例:格式化代码
- `go vet`
- 示例:运行 `go vet`
- `go test`
- 示例:运行测试
- `go build`
- 示例:构建项目
- Web 框架
- Gin
- 示例:使用 Gin 创建一个简单的 Web 服务器
- Echo
- 示例:使用 Echo 创建一个简单的 Web 服务器
- Beego
- 示例:使用 Beego 创建一个简单的 Web 服务器
- ORM 框架
- GORM
- 示例:使用 GORM 操作数据库
- XORM
- 示例:使用 XORM 操作数据库
并发编程
Go 语言的并发编程是其一大亮点。通过 goroutine
和 channel
,Go 使得并发编程变得简洁且高效。Go 还提供了 sync
包,包含了多种同步原语,帮助开发者在并发程序中处理共享数据和同步问题。
goroutine:创建和调度 goroutine
goroutine
是 Go 中最基本的并发单元,可以认为是轻量级的线程。通过 go
关键字可以轻松创建一个新的 goroutine
,并发执行指定的函数或方法。
package mainimport ("fmt""time"
)// 一个简单的并发任务
func sayHello() {time.Sleep(1 * time.Second)fmt.Println("Hello from goroutine!")
}func main() {go sayHello() // 创建一个 goroutinefmt.Println("Hello from main!")time.Sleep(2 * time.Second) // 等待 goroutine 执行完毕
}
在这个示例中,go sayHello()
启动了一个新的 goroutine
,并发执行 sayHello
函数。主函数继续执行并打印输出,最后通过 time.Sleep
等待 goroutine
完成。
channel:无缓冲 channel、有缓冲 channel、select 语句
channel
是 Go 语言用于不同 goroutine
之间通信的机制。通过 channel
,可以安全地传递数据,避免了数据竞争问题。Go 提供了无缓冲和有缓冲的 channel
,以及 select
语句来处理多个 channel
的操作。
无缓冲 channel
无缓冲 channel
会在发送数据和接收数据时进行同步,确保发送和接收操作相互配合。
package mainimport "fmt"func main() {ch := make(chan string) // 创建一个无缓冲 channel// 启动 goroutine 发送数据go func() {ch <- "Hello from goroutine!" // 向 channel 发送数据}()// 接收数据msg := <-chfmt.Println(msg) // 输出: Hello from goroutine!
}
有缓冲 channel
有缓冲的 channel
可以在发送数据时不必立即等待接收方,直到缓冲区满或接收方取走数据。创建有缓冲 channel
时,可以指定缓冲区的大小。
package mainimport "fmt"func main() {ch := make(chan string, 2) // 创建一个缓冲区大小为 2 的 channelch <- "Hello"ch <- "World" // 向 channel 发送两个消息fmt.Println(<-ch) // 输出: Hellofmt.Println(<-ch) // 输出: World
}
在这个示例中,channel
的缓冲区大小为 2,可以在不立即接收的情况下向 channel
发送两个数据。
select 语句
select
语句允许你等待多个 channel
操作。它类似于 switch
语句,但用于处理多个 channel
的发送或接收。
package mainimport ("fmt""time"
)func main() {ch1 := make(chan string)ch2 := make(chan string)go func() {time.Sleep(1 * time.Second)ch1 <- "Message from ch1"}()go func() {time.Sleep(2 * time.Second)ch2 <- "Message from ch2"}()// 使用 select 语句监听多个 channelselect {case msg1 := <-ch1:fmt.Println("Received:", msg1)case msg2 := <-ch2:fmt.Println("Received:", msg2)}
}
在这个示例中,select
会等待 ch1
或 ch2
中的任一 channel
可用。当第一个 channel
可用时,它会立即执行对应的 case
,并停止等待。
sync 包:Mutex、RWMutex、WaitGroup 等同步原语
Go 提供了 sync
包中的多种同步原语,用于处理并发程序中的共享资源访问问题。
Mutex:互斥锁
Mutex
是最常用的同步原语,它确保同一时刻只有一个 goroutine
可以访问共享资源。通过 Lock
和 Unlock
方法来加锁和解锁。
package mainimport ("fmt""sync"
)var counter int
var mutex sync.Mutexfunc increment() {mutex.Lock() // 加锁counter++mutex.Unlock() // 解锁
}func main() {var wg sync.WaitGroup// 启动多个 goroutine 进行并发操作for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait() // 等待所有 goroutine 执行完毕fmt.Println("Final counter:", counter)
}
在这个示例中,使用 Mutex
来保证对 counter
的并发访问是安全的。
RWMutex:读写互斥锁
RWMutex
是一种读写互斥锁,它允许多个 goroutine
同时读取共享资源,但在写操作时会阻止其他的读写操作。
package mainimport ("fmt""sync"
)var counter int
var rwMutex sync.RWMutexfunc read() int {rwMutex.RLock() // 读锁defer rwMutex.RUnlock()return counter
}func write() {rwMutex.Lock() // 写锁counter++rwMutex.Unlock()
}func main() {var wg sync.WaitGroup// 启动多个 goroutine 进行读写操作for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()write()}()}for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()read()}()}wg.Wait()fmt.Println("Final counter:", counter)
}
在这个示例中,RWMutex
允许多个 goroutine
同时进行读操作,但在执行写操作时会锁住资源。
WaitGroup:等待多个 goroutine 完成
WaitGroup
用于等待一组 goroutine
执行完毕。它提供了 Add
、Done
和 Wait
方法来控制并发流程。
package mainimport ("fmt""sync"
)func task(wg *sync.WaitGroup) {defer wg.Done() // 执行完毕时调用 Donefmt.Println("Task completed")
}func main() {var wg sync.WaitGroup// 启动多个 goroutinefor i := 0; i < 5; i++ {wg.Add(1) // 增加等待的 goroutine 数量go task(&wg)}wg.Wait() // 等待所有 goroutine 执行完毕fmt.Println("All tasks completed")
}
在这个示例中,WaitGroup
用于等待多个并发任务完成。
以下是网络编程部分的详细介绍,涵盖了 TCP/UDP 编程、HTTP 编程和 WebSocket 编程的内容:
网络编程
Go 语言提供了强大的网络编程能力,支持 TCP/UDP 协议的开发、HTTP 服务的构建,以及 WebSocket 协议的支持。通过内置的 net
和 net/http
包,Go 使得网络编程变得简洁高效。我们也可以使用第三方库,如 gorilla/websocket
来简化 WebSocket 的使用。
TCP/UDP 编程:net
包的使用
Go 的 net
包提供了多种方法来处理 TCP 和 UDP 网络编程。你可以通过它创建网络连接、发送和接收数据。
TCP 编程
TCP 是面向连接的协议,它保证数据的可靠传输。在 Go 中,可以使用 net
包提供的 Dial
和 Listen
方法来建立 TCP 客户端和服务器。
TCP 服务器
package mainimport ("fmt""net""log"
)func handleConnection(conn net.Conn) {fmt.Println("Client connected:", conn.RemoteAddr())defer conn.Close()conn.Write([]byte("Hello, client!"))
}func main() {// 创建 TCP 监听listener, err := net.Listen("tcp", ":8080")if err != nil {log.Fatal(err)}defer listener.Close()fmt.Println("Server is listening on port 8080...")for {conn, err := listener.Accept() // 接受客户端连接if err != nil {log.Fatal(err)}go handleConnection(conn) // 为每个连接启动一个 goroutine 处理}
}
TCP 客户端
package mainimport ("fmt""net""log"
)func main() {// 创建 TCP 客户端连接conn, err := net.Dial("tcp", "localhost:8080")if err != nil {log.Fatal(err)}defer conn.Close()// 接收并打印服务器返回的消息buffer := make([]byte, 1024)n, err := conn.Read(buffer)if err != nil {log.Fatal(err)}fmt.Println("Server says:", string(buffer[:n]))
}
UDP 编程
UDP 是无连接的协议,传输速度较快,但不保证数据的可靠性。在 Go 中,使用 net
包提供的 ListenUDP
和 DialUDP
方法来处理 UDP 通信。
UDP 服务器
package mainimport ("fmt""net""log"
)func main() {// 创建 UDP 监听地址addr, err := net.ResolveUDPAddr("udp", ":8080")if err != nil {log.Fatal(err)}conn, err := net.ListenUDP("udp", addr)if err != nil {log.Fatal(err)}defer conn.Close()buffer := make([]byte, 1024)for {n, clientAddr, err := conn.ReadFromUDP(buffer)if err != nil {log.Fatal(err)}fmt.Println("Received:", string(buffer[:n]), "from", clientAddr)}
}
UDP 客户端
package mainimport ("fmt""net""log"
)func main() {// 创建 UDP 目标地址serverAddr, err := net.ResolveUDPAddr("udp", "localhost:8080")if err != nil {log.Fatal(err)}// 创建 UDP 连接conn, err := net.DialUDP("udp", nil, serverAddr)if err != nil {log.Fatal(err)}defer conn.Close()// 发送数据message := []byte("Hello, UDP server!")_, err = conn.Write(message)if err != nil {log.Fatal(err)}fmt.Println("Message sent to server")
}
HTTP 编程:net/http
包的使用,编写 HTTP 服务器和客户端
Go 语言的 net/http
包提供了简单而强大的 HTTP 服务器和客户端功能。通过该包,你可以很容易地编写 HTTP 服务器,并与 HTTP 客户端进行通信。
HTTP 服务器
Go 提供了非常简洁的方式来构建 HTTP 服务器。你只需要实现处理请求的函数,并通过 http.HandleFunc
注册路由,然后调用 http.ListenAndServe
启动服务器。
package mainimport ("fmt""net/http"
)func helloHandler(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, World!")
}func main() {http.HandleFunc("/hello", helloHandler) // 注册路由fmt.Println("Starting server on :8080...")http.ListenAndServe(":8080", nil) // 启动 HTTP 服务器
}
在这个示例中,HTTP 服务器会监听 8080
端口,并且在请求 /hello
路径时返回 Hello, World!
。
HTTP 客户端
Go 语言的 http
包也提供了简单的方式来发起 HTTP 请求。你可以使用 http.Get
、http.Post
等方法发起请求,并接收返回的响应。
package mainimport ("fmt""net/http""log"
)func main() {// 发起 GET 请求resp, err := http.Get("http://localhost:8080/hello")if err != nil {log.Fatal(err)}defer resp.Body.Close()// 打印响应状态和内容fmt.Println("Response Status:", resp.Status)
}
在这个示例中,客户端发起一个 GET 请求,访问服务器的 /hello
路径,获取服务器的响应。
WebSocket 编程:gorilla/websocket
包的使用
WebSocket 是一种在单个连接上进行全双工通信的协议。Go 中可以使用第三方库 gorilla/websocket
来实现 WebSocket 服务端和客户端。
WebSocket 服务器
首先,你需要安装 gorilla/websocket
包:
go get github.com/gorilla/websocket
然后,编写 WebSocket 服务器:
package mainimport ("fmt""log""net/http""github.com/gorilla/websocket"
)var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true },
}func handleConnection(w http.ResponseWriter, r *http.Request) {conn, err := upgrader.Upgrade(w, r, nil)if err != nil {log.Println(err)return}defer conn.Close()for {messageType, p, err := conn.ReadMessage()if err != nil {log.Println(err)return}if err := conn.WriteMessage(messageType, p); err != nil {log.Println(err)return}}
}func main() {http.HandleFunc("/ws", handleConnection)fmt.Println("WebSocket server is running on :8080...")log.Fatal(http.ListenAndServe(":8080", nil))
}
WebSocket 客户端
WebSocket 客户端同样也可以使用 gorilla/websocket
包来连接到 WebSocket 服务器。
package mainimport ("fmt""log""github.com/gorilla/websocket"
)func main() {// 连接到 WebSocket 服务器conn, _, err := websocket.DefaultDialer.Dial("ws://localhost:8080/ws", nil)if err != nil {log.Fatal(err)}defer conn.Close()message := []byte("Hello, WebSocket server!")err = conn.WriteMessage(websocket.TextMessage, message)if err != nil {log.Fatal(err)}_, response, err := conn.ReadMessage()if err != nil {log.Fatal(err)}fmt.Println("Received from server:", string(response))
}
在这个示例中,WebSocket 客户端连接到 WebSocket 服务器并发送消息,然后等待服务器的响应。
数据库操作
Go 语言通过内置的 database/sql
包支持关系型数据库的操作,同时也支持通过第三方库与 NoSQL 数据库进行交互。你可以通过标准的 SQL 操作与关系型数据库(如 MySQL、PostgreSQL)进行交互,也可以使用专门的库来连接 NoSQL 数据库(如 MongoDB、Redis)。本篇博客将介绍如何在 Go 中操作关系型数据库与 NoSQL 数据库。
使用 database/sql
包操作关系型数据库
Go 的 database/sql
包提供了一个统一的接口,允许与多种关系型数据库(如 MySQL、PostgreSQL 等)进行交互。你可以通过 database/sql
包提供的 API 执行 SQL 查询、插入、更新和删除等操作。
连接数据库
首先,你需要安装并导入适用于数据库的驱动,例如对于 MySQL,你可以使用 github.com/go-sql-driver/mysql
驱动,针对 PostgreSQL,可以使用 github.com/lib/pq
。
安装 MySQL 驱动
go get -u github.com/go-sql-driver/mysql
连接到 MySQL 数据库
package mainimport ("fmt""log""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {// 连接数据库dsn := "root:password@tcp(127.0.0.1:3306)/testdb"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()// 测试数据库连接if err := db.Ping(); err != nil {log.Fatal(err)}fmt.Println("Successfully connected to MySQL database")
}
执行 SQL 查询
执行 SQL 查询时,使用 Query
或 QueryRow
方法获取数据。
查询单行数据
package mainimport ("fmt""log""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {dsn := "root:password@tcp(127.0.0.1:3306)/testdb"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()// 查询单行数据var name stringerr = db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)if err != nil {log.Fatal(err)}fmt.Println("Name:", name)
}
查询多行数据
package mainimport ("fmt""log""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {dsn := "root:password@tcp(127.0.0.1:3306)/testdb"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()rows, err := db.Query("SELECT id, name FROM users")if err != nil {log.Fatal(err)}defer rows.Close()for rows.Next() {var id intvar name stringif err := rows.Scan(&id, &name); err != nil {log.Fatal(err)}fmt.Println(id, name)}if err := rows.Err(); err != nil {log.Fatal(err)}
}
执行 SQL 插入、更新和删除
插入数据
package mainimport ("fmt""log""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {dsn := "root:password@tcp(127.0.0.1:3306)/testdb"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()// 插入数据result, err := db.Exec("INSERT INTO users(name) VALUES(?)", "Alice")if err != nil {log.Fatal(err)}lastInsertID, err := result.LastInsertId()if err != nil {log.Fatal(err)}fmt.Println("Inserted record with ID:", lastInsertID)
}
更新数据
package mainimport ("fmt""log""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {dsn := "root:password@tcp(127.0.0.1:3306)/testdb"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()// 更新数据result, err := db.Exec("UPDATE users SET name = ? WHERE id = ?", "Bob", 1)if err != nil {log.Fatal(err)}affectedRows, err := result.RowsAffected()if err != nil {log.Fatal(err)}fmt.Println("Affected rows:", affectedRows)
}
删除数据
package mainimport ("fmt""log""database/sql"_ "github.com/go-sql-driver/mysql"
)func main() {dsn := "root:password@tcp(127.0.0.1:3306)/testdb"db, err := sql.Open("mysql", dsn)if err != nil {log.Fatal(err)}defer db.Close()// 删除数据result, err := db.Exec("DELETE FROM users WHERE id = ?", 1)if err != nil {log.Fatal(err)}affectedRows, err := result.RowsAffected()if err != nil {log.Fatal(err)}fmt.Println("Affected rows:", affectedRows)
}
使用 NoSQL 数据库
Go 语言也支持与 NoSQL 数据库进行交互,如 MongoDB 和 Redis。我们将介绍如何使用 Go 操作这两种数据库。
使用 MongoDB
MongoDB 是一个文档型 NoSQL 数据库,可以通过 go.mongodb.org/mongo-driver
驱动与 MongoDB 进行交互。
安装 MongoDB 驱动
go get go.mongodb.org/mongo-driver/mongo
连接到 MongoDB 并执行查询
package mainimport ("fmt""log""context""go.mongodb.org/mongo-driver/mongo""go.mongodb.org/mongo-driver/mongo/options"
)func main() {client, err := mongo.Connect(context.TODO(), options.Client().ApplyURI("mongodb://localhost:27017"))if err != nil {log.Fatal(err)}defer client.Disconnect(context.TODO())collection := client.Database("testdb").Collection("users")var result map[string]interface{}err = collection.FindOne(context.TODO(), map[string]interface{}{"name": "Alice"}).Decode(&result)if err != nil {log.Fatal(err)}fmt.Println("Found user:", result)
}
使用 Redis
Redis 是一个键值存储数据库,可以使用 github.com/go-redis/redis/v8
库与 Redis 进行交互。
安装 Redis 驱动
go get github.com/go-redis/redis/v8
连接到 Redis 并执行操作
package mainimport ("fmt""log""github.com/go-redis/redis/v8""context"
)func main() {rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379", // Redis 地址})ctx := context.Background()// 设置键值对err := rdb.Set(ctx, "name", "Alice", 0).Err()if err != nil {log.Fatal(err)}// 获取键值对val, err := rdb.Get(ctx, "name").Result()if err != nil {log.Fatal(err)}fmt.Println("name:", val)
}
测试和性能优化
Go 语言提供了强大的测试框架来进行单元测试、基准测试,并通过 pprof
工具进行性能分析。在这部分内容中,我们将介绍如何编写单元测试和基准测试,使用 pprof
进行性能分析,以及一些常见的代码优化技巧。
编写单元测试和基准测试
Go 语言内置了 testing
包来支持单元测试,使用该包可以轻松编写和运行测试用例。此外,Go 也支持基准测试(Benchmarking),通过基准测试可以测试代码的性能。
单元测试
单元测试是为了验证某个函数或方法的行为是否符合预期。你可以使用 testing.T
类型来编写测试函数,并通过 t.Error
或 t.Fail
来报告失败。
示例:编写单元测试
package mainimport ("testing"
)// 被测试的函数
func Add(a, b int) int {return a + b
}// 测试 Add 函数
func TestAdd(t *testing.T) {result := Add(2, 3)if result != 5 {t.Errorf("Add(2, 3) = %d; want 5", result)}
}
运行单元测试:
go test
基准测试
基准测试用于衡量代码的执行时间,通常用于性能优化。基准测试函数以 Benchmark
开头,接受一个 *testing.B
类型的参数,调用 b.N
来运行多次测试。
示例:编写基准测试
package mainimport ("testing"
)// 被基准测试的函数
func Multiply(a, b int) int {return a * b
}// 基准测试 Multiply 函数
func BenchmarkMultiply(b *testing.B) {for i := 0; i < b.N; i++ {Multiply(2, 3)}
}
运行基准测试:
go test -bench .
使用 pprof 进行性能分析
Go 语言提供了 pprof
包来进行性能分析。pprof
可以帮助你识别程序中的性能瓶颈,例如 CPU 使用率、内存分配等。
启用 pprof
要启用 pprof,首先需要在程序中导入 net/http/pprof
包,并启动一个 HTTP 服务器来暴露 pprof 端点。
示例:启用 pprof
package mainimport ("fmt""net/http"_ "net/http/pprof" // 引入 pprof 包"log"
)func main() {// 启动 pprof 服务go func() {log.Println(http.ListenAndServe("localhost:6060", nil))}()// 其他应用逻辑fmt.Println("Server is running...")select {}
}
你可以通过访问 http://localhost:6060/debug/pprof/
来查看程序的性能分析信息。
使用 pprof 工具分析性能
在运行程序时,你可以使用 pprof
工具生成性能报告,并通过命令行查看和分析。
示例:生成 CPU 性能报告
go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30
示例:生成内存使用报告
go tool pprof http://localhost:6060/debug/pprof/heap
分析报告
通过 go tool pprof
工具生成的报告,你可以查看函数的调用次数、占用的 CPU 时间以及内存使用情况等信息。这个工具非常适合于找出代码中性能瓶颈,帮助优化性能。
代码优化技巧
Go 提供了一些工具和技巧来帮助你优化代码的性能。以下是一些常见的优化技巧:
减少内存分配
内存分配是影响性能的一个重要因素,频繁的内存分配可能导致垃圾回收器的开销增大,进而影响程序性能。尽量避免不必要的内存分配,使用对象池等技术来减少分配。
示例:重用切片
package mainimport "fmt"func main() {var data []intfor i := 0; i < 1000000; i++ {data = append(data, i)}// 使用重用的切片而非每次都创建新的data = append(data[:0], data...)fmt.Println("Slice reused")
}
避免锁竞争
并发程序中,锁竞争是常见的性能瓶颈。当多个 Goroutine 同时访问共享资源时,如果使用锁(如 sync.Mutex
)不当,可能导致程序性能下降。尽量减少锁的使用范围,并考虑使用更轻量的同步原语,例如 sync/atomic
或 sync.RWMutex
。
示例:避免过多锁的使用
package mainimport ("sync""fmt"
)var counter int
var mu sync.Mutexfunc increment() {mu.Lock()counter++mu.Unlock()
}func main() {var wg sync.WaitGroupfor i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()increment()}()}wg.Wait()fmt.Println("Counter:", counter)
}
在上述代码中,每次对 counter
的操作都需要锁住共享资源。为了优化性能,使用细粒度的锁,或者尝试使用其他并发控制方法。
使用更高效的算法和数据结构
根据业务需求选择最合适的算法和数据结构是提升性能的关键。例如,使用哈希表(map
)进行快速查找,或者使用更高效的排序算法来减少时间复杂度。
示例:使用哈希表代替线性查找
package mainimport "fmt"func main() {// 使用 map 来替代线性查找data := []string{"apple", "banana", "cherry"}lookup := make(map[string]bool)for _, item := range data {lookup[item] = true}if lookup["banana"] {fmt.Println("Found banana!")} else {fmt.Println("Banana not found.")}
}
避免重复计算
缓存计算结果避免重复计算,是常见的性能优化技巧。可以使用 map
或者缓存框架来存储已经计算过的结果。
示例:缓存计算结果
package mainimport "fmt"var cache = make(map[int]int)func fib(n int) int {if result, exists := cache[n]; exists {return result}if n <= 1 {return n}result := fib(n-1) + fib(n-2)cache[n] = resultreturn result
}func main() {fmt.Println(fib(10)) // 55
}
工具和框架
在 Go 语言开发过程中,掌握常用的工具和框架能够提高开发效率和代码质量。Go 提供了许多内置工具来帮助开发者进行代码格式化、静态检查、单元测试等操作。同时,Go 的 Web 框架和 ORM 框架也大大简化了 Web 应用和数据库交互的开发工作。本文将介绍 Go 中的常用工具、Web 框架和 ORM 框架。
常用工具
Go 语言提供了一系列命令行工具来帮助开发者进行常见的开发任务,包括代码格式化、静态检查、单元测试、构建和安装等。
go fmt
go fmt
是 Go 语言中的代码格式化工具,它会自动格式化代码,使代码风格统一。Go 语言的代码格式非常严格,因此使用 go fmt
可以保证项目中的代码风格一致。
示例:格式化代码
go fmt main.go
运行后,Go 会自动调整 main.go
文件中的代码格式,确保符合 Go 语言的格式要求。
go vet
go vet
是 Go 语言中的静态分析工具,能够帮助开发者发现潜在的错误或不规范的代码。它并不会执行程序,而是检查代码中的常见问题,比如未使用的变量、不必要的类型转换等。
示例:运行 go vet
go vet main.go
go vet
会检查代码中的潜在问题,并报告错误或警告,帮助开发者避免常见的编程错误。
go test
go test
是 Go 语言中的测试工具,用于执行单元测试。它会自动运行你在代码中编写的所有测试函数,并报告测试结果。
示例:运行测试
go test
如果你的项目中有一个或多个测试文件,运行 go test
会自动执行这些测试并输出测试结果。
go build
go build
用于构建 Go 项目的可执行文件。它会将源代码编译为一个二进制文件,可以直接运行。
示例:构建项目
go build -o myapp
这会将当前目录下的 Go 代码编译成名为 myapp
的可执行文件。
Web 框架
Go 语言的 Web 框架帮助开发者更高效地构建 Web 应用程序。常用的 Web 框架包括 Gin、Echo 和 Beego 等。每个框架都有不同的特点和适用场景。
Gin
Gin 是一个高性能的 Web 框架,特别适合于需要处理高并发的 Web 应用。它的设计目标是尽可能减少内存分配,从而提高性能。Gin 提供了丰富的路由功能、中间件支持以及易于使用的 JSON 处理功能。
示例:使用 Gin 创建一个简单的 Web 服务器
package mainimport ("github.com/gin-gonic/gin"
)func main() {r := gin.Default()r.GET("/hello", func(c *gin.Context) {c.JSON(200, gin.H{"message": "Hello, World!",})})r.Run(":8080")
}
Gin 非常适合开发高性能的 RESTful API 和 Web 应用。
Echo
Echo 是一个极简的 Web 框架,注重高性能和易用性。它支持路由、中间件、请求/响应绑定等功能,并且具有极低的内存占用和高吞吐量。
示例:使用 Echo 创建一个简单的 Web 服务器
package mainimport ("github.com/labstack/echo/v4""net/http"
)func main() {e := echo.New()e.GET("/hello", func(c echo.Context) error {return c.JSON(http.StatusOK, map[string]string{"message": "Hello, World!"})})e.Logger.Fatal(e.Start(":8080"))
}
Echo 简洁高效,适合用于构建高性能的 Web 服务。
Beego
Beego 是一个全栈的 Web 框架,提供了更多的功能,适合用于开发大型 Web 应用程序。Beego 支持自动化路由、数据模型、表单验证、会话管理等功能,并且内置了 ORM 和任务调度系统。
示例:使用 Beego 创建一个简单的 Web 服务器
package mainimport ("github.com/astaxie/beego"
)func main() {beego.Router("/", &MainController{})beego.Run()
}type MainController struct {beego.Controller
}func (c *MainController) Get() {c.Ctx.WriteString("Hello, World!")
}
Beego 提供了更多的内置功能,适合用于开发完整的 Web 应用程序。
ORM 框架
Go 语言的 ORM 框架帮助开发者更方便地与数据库进行交互。ORM 框架可以将数据库中的表映射为 Go 结构体,简化了 SQL 查询操作。
GORM
GORM 是 Go 语言中最常用的 ORM 框架之一,它支持 MySQL、PostgreSQL、SQLite 和 SQL Server 等数据库,并且提供了丰富的功能,包括模型映射、关联查询、事务管理等。
示例:使用 GORM 操作数据库
package mainimport ("github.com/jinzhu/gorm"_ "github.com/jinzhu/gorm/dialects/sqlite""log"
)type User struct {ID uintName stringAge int
}func main() {db, err := gorm.Open("sqlite3", "test.db")if err != nil {log.Fatal(err)}defer db.Close()db.AutoMigrate(&User{})db.Create(&User{Name: "John", Age: 30})
}
GORM 提供了直观的 API 来操作数据库,支持多种数据库关系和查询方式。
XORM
XORM 是另一个轻量级的 ORM 框架,提供了简单易用的接口来操作数据库。它支持 MySQL、PostgreSQL、SQLite、Oracle 等数据库,并且具有较高的性能和灵活性。
示例:使用 XORM 操作数据库
package mainimport ("github.com/go-xorm/xorm"_ "github.com/go-sql-driver/mysql""log"
)type User struct {ID int64Name stringAge int
}func main() {engine, err := xorm.NewEngine("mysql", "root:password@/test")if err != nil {log.Fatal(err)}defer engine.Close()engine.Sync2(new(User))user := User{Name: "John", Age: 30}engine.Insert(&user)
}
XORM 提供了简单的数据库操作功能,并且支持动态 SQL 查询和自定义数据库操作。
相关文章:
【Go语言快速上手】第二部分:Go语言进阶
文章目录 并发编程goroutine:创建和调度 goroutinechannel:无缓冲 channel、有缓冲 channel、select 语句无缓冲 channel有缓冲 channelselect 语句 sync 包:Mutex、RWMutex、WaitGroup 等同步原语Mutex:互斥锁RWMutex:…...
自定义多功能输入对话框:基于 Qt 打造灵活交互界面
一、引言 在使用 Qt 进行应用程序开发时,我们经常需要与用户进行交互,获取他们输入的各种信息。QInputDialog 是 Qt 提供的一个便捷工具,可用于简单的输入场景,但当需求变得复杂,需要支持更多类型的输入控件࿰…...
计算机毕业设计SparkStreaming+Kafka广告推荐系统 广告预测 广告数据分析可视化 广告爬虫 大数据毕业设计 深度学习 机器学习
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
企业FTP替代升级,实现传输大文件提升100倍!
随着信息技术的飞速发展,网络安全环境也变得越来越复杂。在这种背景下,传统的FTP(文件传输协议)已经很难满足现代企业对文件传输的需求了。FTP虽然用起来简单,但它的局限性和安全漏洞让它在面对高效、安全的数据交换时…...
盘姬工具箱:完全免费的电脑工具箱
今天给大家介绍一个非常好用的系统工具箱,里面内含100多个工具,完全免费使用,而且没有广告,非常的棒。 盘姬工具箱:完全免费的电脑工具箱 盘姬工具箱是一款完全免费的电脑工具箱,功能丰富且实用。软件下载并…...
【个人开发】macbook m1 Lora微调qwen大模型
本项目参考网上各类教程整理而成,为个人学习记录。 项目github源码地址:Lora微调大模型 项目中微调模型为:qwen/Qwen1.5-4B-Chat。 去年新发布的Qwen/Qwen2.5-3B-Instruct同样也适用。 微调步骤 step0: 环境准备 conda create --name fin…...
开源项目OpenIM单机部署生产环境异常处理及数据恢复
在生产环境中,通常会采用集群部署来保证组件和服务的高可用性。然而,在资源有限的情况下,一些开发者可能会选择在生产环境中进行单机部署(使用源码部署或docker容器)。本文将介绍在单机部署环境下如何进行数据备份、异…...
天津三石峰科技——汽车生产厂的设备振动检测项目案例
汽车产线有很多传动设备需要长期在线运行,会出现老化、疲劳、磨损等 问题,为了避免意外停机造成损失,需要加装一些健康监测设备,监测设备运 行状态。天津三石峰科技采用 12 通道振动信号采集卡(下图 1)对…...
MySQL-5.7.44安装(CentOS7)
目录 1、下载安装包并解压 2、创建数据目录与日志目录 3、设置环境变量 4、刷新环境变量 5、执行初始化 6、创建配置文件目录 7、新建配置文件 8、为安装目录赋予可执行权限 9、创建服务启动脚本 10、启动服务并将启动脚本加入开机自启动 11、查看服务状态 12、创建…...
什么是网络安全
1) 什么是网络安全 作为程序员,主要是面向产品的安全的问题。比如sql注入,xss,csrf,cookie窃取等等,都值得我们去思考。保证网站运行正常,客户数据安全。 2) sql注入 简单的说,就是利用表单提…...
即时通讯开源项目OpenIM配置离线推送全攻略
如何进行二次开发 如果您需要基于 OpenIM 开发新特性,首先要确定是针对业务侧还是即时通讯核心逻辑。 由于 OpenIM 系统本身已经做好了比较多的抽象,大部分聊天的功能已经具备了,不建议修改 IM 本身。 如果需要增加 IM 的能力,可以…...
快速上手——.net封装使用DeekSeek-V3 模型
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,用爱发电,去丈量人心,是否能达到人机合一?开工大吉 新的一年就这么水灵灵的开始了,在这里,祝各位读者新春快乐,万事如意! 新年伊…...
【原创】Android Studio Ladybug 中Gradle配置
使用Android Studio创建项目后,由于需要下载的一下文件在国外,加上网速的问题,以及防火墙的问题,不少文件难以下载。常常导致项目创建后,要等很长时间,各种折腾,结果一个demo都跑不起来。 经过…...
Java版本与JDK版本
两者关联 Java版本指的Java语言和平台的版本,例如Java8、Java11、Java17等,每个版本会引入新特性、改进和修复。 JDK(Java Development Kit)版本则是开发工具包,包含编译器、调试器等工具,通常与Java版本对应,例如JDK…...
【GeeRPC】Day3:服务注册(Service Register)
Day3:服务注册(Service Register) 今天的任务是: 通过反射实现服务注册功能;在服务端实现服务调用,代码约 150 行; 结构体映射为服务 RPC 框架的一个基本能力是:像调用本地程序一…...
c/c++蓝桥杯经典编程题100道(17)二叉树遍历
二叉树遍历 ->返回c/c蓝桥杯经典编程题100道-目录 目录 二叉树遍历 一、题型解释 二、例题问题描述 三、C语言实现 解法1:递归前序遍历(难度★) 解法2:迭代中序遍历(难度★★) 解法3:…...
mysql系统库介绍,数据字典(介绍,存储方式,常见表,访问权限),系统表(介绍,不同功能的表)
目录 mysql系统库 介绍 数据字典 介绍 不同版本下的存储方式 常见的数据字典表 访问权限 系统表 介绍 权限授予系统表 对象信息系统表 服务器端帮助系统表 时区系统表 mysql系统库 介绍 MySQL 默认创建 的特殊数据库,主要用于存储服务器运行时所需的信…...
如何在macOS上安装Ollama
安装Ollama 安装Ollama的步骤相对简单,以下是基本的安装指南: 访问官方网站:打开浏览器,访问Ollama的官方网站。 下载安装包:根据你的操作系统,选择相应的安装包进行下载。 运行安装程序:下载完…...
【JavaScript】《JavaScript高级程序设计 (第4版) 》笔记-Chapter6-集合引用类型
六、集合引用类型 Object 是 ECMAScript 中最常用的类型之一。虽然 Object 的实例没有多少功能,但很适合存储和在应用程序间交换数据。 显式地创建 Object 的实例有两种方式。第一种是使用 new 操作符和 Object 构造函数。另一种方式是使用对象字面量(ob…...
Spring Boot Actuator使用
说明:本文介绍Spring Boot Actuator的使用,关于Spring Boot Actuator介绍,下面这篇博客写得很好,珠玉在前,我就不多介绍了。 Spring Boot Actuator 简单使用 项目里引入下面这个依赖 <!--Spring Boot Actuator依…...
SwanLab x verl:可视化LLM强化学习后训练教程
文章目录 介绍Verl和SwanLab1. 环境安装2. 使用方法3. 查看训练日志 介绍Verl和SwanLab verl 是一个灵活、高效且可用于生产环境的强化学习(RL)训练框架,专为大型语言模型(LLMs)的后训练设计。它由字节跳动火山引擎团…...
linux安装oracle19c
安装 安装前检查配置: 挂载50g盘: vgcreate oravg /dev/sdb lvcreate -L 49.8G -n oralv oravg lvscan mkfs.xfs /dev/oravg/oralv 查看uuid blkid 复制分区表 cp /etc/fstab /etc/fstab.bakvi /etc/fstab内容为: /dev/oravg/oralv /u01 xfs defau…...
半导体制造工艺讲解
目录 一、半导体制造工艺的概述 二、单晶硅片的制造 1.单晶硅的制造 2.晶棒的切割、研磨 3.晶棒的切片、倒角和打磨 4.晶圆的检测和清洗 三、晶圆制造 1.氧化与涂胶 2.光刻与显影 3.刻蚀与脱胶 4.掺杂与退火 5.薄膜沉积、金属化和晶圆减薄 6.MOSFET在晶圆表面的形…...
VMware下Linux和macOS安装VSCode一些总结
本文介绍VMware下Linux和macOS安装VSCode的一些内容,包括VSCode编译器显示中文以及安装.NET环境和Python环境。 VSCode下载地址:Download Visual Studio Code - Mac, Linux, Windows 一.Linux系统下 1.安装中文包 按 Ctrl Shift P 打开命令面板。输…...
STC51 单片机中,定时器 / 计数器相关的寄存器
在 STC51 单片机中,定时器 / 计数器相关的寄存器主要有定时器控制寄存器(TCON)、定时器工作方式寄存器(TMOD)以及定时器初值寄存器(TH0、TL0、TH1、TL1),下面详细解释这些寄存器各位…...
DeepSeek与人工智能的结合:探索搜索技术的未来
云边有个稻草人-CSDN博客 目录 引言 一、DeepSeek的技术背景 1.1 传统搜索引擎的局限性 1.2 深度学习在搜索中的优势 二、DeepSeek与人工智能的结合 2.1 自然语言处理(NLP) 示例代码:基于BERT的语义搜索 2.2 多模态搜索 示例代码&…...
OpenCV:图像修复
目录 简述 1. 原理说明 1.1 Navier-Stokes方法(INPAINT_NS) 1.2 快速行进方法(INPAINT_TELEA) 2. 实现步骤 2.1 输入图像和掩膜(Mask) 2.2 调用cv2.inpaint()函数 2.3 完整代码示例 2.4 运行结果 …...
解决基于FastAPI Swagger UI的文档打不开的问题
基于FastAPI Swagger UI的文档链接/docs和/redoc在没有外网的状态下无法打开,原因是Swagger依赖的JS和CSS来自CDN。 https://cdn.jsdelivr.net/npm/swagger-ui-dist5/swagger-ui-bundle.js https://cdn.jsdelivr.net/npm/swagger-ui-dist5/swagger-ui.css https://…...
前端开发知识梳理 - HTMLCSS
1. 盒模型 由内容区(content)、内边距(padding)、边框(border)和外边距(margin)组成。 (1)标准盒模型(box-sizing默认值, content-boxÿ…...
Win10环境使用ChatBox集成Deep Seek解锁更多玩法
Win10环境使用ChatBox集成Deep Seek解锁更多玩法 前言 之前部署了14b的Deep Seek小模型,已经验证了命令行及接口方式的可行性。但是纯命令行或者PostMan方式调用接口显然不是那么友好: https://lizhiyong.blog.csdn.net/article/details/145505686 纯…...
LM Studio 部署本地大语言模型
一、下载安装 1.搜索:lm studio LM Studio - Discover, download, and run local LLMs 2.下载 3.安装 4.更改成中文 二、下载模型(软件内下载) 1.选择使用代理,否则无法下载 2.更改模型下载目录 默认下载位置 C:\Users\用户名\.lmstudio\models 3.搜…...
Qt:QWidget核心属性
目录 QWidget核心属性 enab geometry WindowFrame的影响 windowTitle windowIcon qrc文件管理资源 windowOpacity cursor font toolTip focusPolicy styleSheet QWidget核心属性 在Qt中使用QWidget类表示"控件",如按钮、视图、输入框、滚动…...
unity学习29:摄像机camera相关skybox 和 Render Texture测试效果
目录 1 摄像机 1.1 每个Scene里都自带一个摄像机 camera 1.2 可以创建多个camera 1.3 下面先看backgroundtype: 2 backgroundtype: 天空盒 skybox 2.1 清除标志,清除:天空盒 自选天空盒 2.2 window /Asset Store 2.3 导入skybox 3 backgroundtype: 纯色…...
吴恩达深度学习——卷积神经网络的特殊应用
内容来自https://www.bilibili.com/video/BV1FT4y1E74V,仅为本人学习使用。 文章目录 人脸识别相关定义Similarity函数使用Siamese网络实现函数d使用Triplet损失学习参数 神经风格迁移深度卷积网络可视化神经风格迁移的代价函数内容损失函数风格损失函数 人脸识别 …...
go语言文件和目录
打开和关闭文件 os.Open()函数能够打开一个文件,返回一个*File 和一个 err。操作完成文件对象以后一定要记得关闭文件。 package mainimport ("fmt""os" )func main() {// 只读方式打开当前目录下的 main.go 文件file, err : os.Open(".…...
c++ 面试题
C 面试题通常涵盖基础知识、面向对象编程、内存管理、模板、STL(标准模板库)等方面。以下是一些常见的 C 面试题及其简要解答,供你参考: 1. C 基础知识 1.1 C 和 C 的区别是什么? C 是 C 的超集,支持面向…...
JAVA安全—FastJson反序列化利用链跟踪autoType绕过
前言 FastJson这个漏洞我们之前讲过了,今天主要是对它的链条进行分析一下,明白链条的构造原理。 Java安全—log4j日志&FastJson序列化&JNDI注入_log4j漏洞-CSDN博客 漏洞版本 1.2.24及以下没有对序列化的类做校验,导致漏洞产生 1.2.25-1.2.41增加了黑名单限制,…...
Java Stream API:高效数据处理的利器引言
Java Stream API:高效数据处理的利器引言 在 Java 编程中,数据处理是一项极为常见且关键的任务。传统的 for 循环在处理数据集合时,往往会导致代码变得冗长、复杂,这不仅增加了代码的编写难度,还降低了代码的可读性和…...
kubeadm构建k8s源码阅读环境
目标 前面看了minikube的源码了解到其本质是调用了kubeadm来启动k8s集群,并没有达到最初看代码的目的。 所以继续看看kubeadm的代码,看看能否用来方便地构建源码调试环境。 k8s源码编译 kubeadm源码在k8s源码库中,所以要先克隆k8s源码。之…...
Java架构设计亿级流量场景下的本地缓存方案选型
在当今的互联网时代,亿级流量的应用场景已经司空见惯。无论是大型电商平台的促销活动,还是热门社交应用的日常运营,都可能面临每秒数万甚至数十万的请求流量。在这样的高并发、高流量场景下,系统的性能和稳定性面临着巨大的挑战。…...
ChatGPT怎么回事?
纯属发现,调侃一下~ 这段时间deepseek不是特别火吗,尤其是它的推理功能,突发奇想,想用deepseek回答一些问题,回答一个问题之后就回复服务器繁忙(估计还在被攻击吧~_~) 然后就转向了GPT…...
离线安装Appium Server
1、问题概述? 安装Appium通常有两种方式: 第一种:下载exe安装包,这种是Appium Server GUI安装方式,缺点是通过命令启动不方便。 第二种:通过cmd安装appium server,可以通过命令方式启动,比较方便。 问题:在没有外网的情况下,无法通过命令在cmd中安装appium server…...
Jetpack ViewModel
private val deviceViewModel: IDeviceViewModel by viewModels<DeviceViewModel>() 这句代码是 Jetpack ViewModel 在 Fragment 或 Activity 中的标准用法,它的作用是 创建并获取 ViewModel 实例,同时确保 ViewModel 的生命周期与 UI 组件保持一…...
2025年2月9日(数据分析,在最高点和最低点添加注释,添加水印)
要在最高点和最低点添加文本注释,可以使用 plt.annotate() 函数。这个函数允许你在图表中的特定位置添加文本注释,并且可以指定箭头指向特定的数据点。 以下是修改后的代码,添加了在最高点和最低点的文本注释: from matplotlib import pyplot as plt from matplotlib imp…...
如何导入第三方sdk | 引入第三方jar 包
0. 背景1. 上传私有仓库2. 使用本地文件系统 0. 背景 对接一些第三方功能,会拿到第三方的sdk,也就是jar包,如何导入呢 1. 上传私有仓库 最好的方式就是将第三方jar包,上传到私有的仓库,这样直接正常在pom引用即可如果只…...
掌握内容中台与人工智能技术的新闻和应用场景分析
内容概要 在当今数字化快速发展的时代,内容中台与人工智能技术的结合为各行各业带来了新的机遇。这一切都源自于对内容生产和管理能力的需求不断提升,尤其在新闻行业中更是如此。内容中台作为一种集中管理内容资源的平台,能够有效整合与调配…...
c#-枚举
//可空类型:int? num 等价 Nullable<int> num Nullable<int> a null; a 99; Console.WriteLine(a);//合并运算符?? : a有值的话,赋值给b int b a ?? 1; Console.WriteLine(b); 枚举成员不能相同,但枚举的值可…...
青少年编程与数学 02-008 Pyhon语言编程基础 22课题、类的定义和使用
青少年编程与数学 02-008 Pyhon语言编程基础 22课题、类的定义和使用 一、类类的定义和使用示例 二、定义1. 类定义语法2. 属性和方法3. 构造器和初始化4. 实例化5. 类变量和实例变量6. 类方法和静态方法7. 继承8. 多态总结 三、使用1. 创建类的实例2. 访问属性3. 调用方法4. 修…...
【通俗易懂说模型】反向传播(附多元回归与Softmax函数)
🌈 个人主页:十二月的猫-CSDN博客 🔥 系列专栏: 🏀深度学习_十二月的猫的博客-CSDN博客 💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 目录 1. 前言 2. …...
【人工智能】Python中的深度学习优化器:从SGD到Adam
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 在深度学习模型的训练过程中,优化器起着至关重要的作用,它决定了模型的收敛速度以及最终的性能。本文将介绍深度学习中常用的优化器,从传…...