Go并发编程与调度器及并发模式详解
Go语言以其简洁的语法和强大的并发能力,成为现代网络编程和微服务架构的热门选择。本文将深入探讨Go的并发编程模型,调度器的工作机制,以及多种并发模式的实现和应用,帮助开发者更好地理解并发编程的设计理念和实践。
Go 的并发模型
在Go中,并发是通过 goroutine 实现的。goroutine 是一个轻量级的线程,Go的调度器负责管理这些goroutine的执行。调度器使用 M:N 模型来调度 goroutine 到操作系统线程,M 表示操作系统线程的数量,N 表示 goroutine 的数量。
Goroutine 的基本使用
package main
import (
"fmt"
"time"
)
func main() {
go sayHello() // 启动一个新的 goroutine
time.Sleep(time.Second) // 确保主程序不会提前退出
}
func sayHello() {
fmt.Println("Hello, Go!")
}
在上面的示例中,sayHello
函数作为一个 goroutine 被启动。由于主程序在运行完 sayHello
之后会立即退出,因此我们使用 time.Sleep
来确保主程序等待一段时间,使 goroutine 有机会执行。
Go 的调度器
Go的调度器使用一种非抢占式调度算法,这意味着goroutine只有在遇到阻塞时才会被调度器切换。调度器使用 G、M 和 P 来表示 goroutine、操作系统线程和执行上下文。G 代表一个 goroutine,M 代表一个操作系统线程,P 代表调度器的上下文。
并发模式
1. 半异步模式
半异步模式是一种混合的并发模式,通常用于长时间运行的操作,如网络请求或文件处理。在这种模式中,任务会异步执行,而结果会通过通道返回。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch <- "结果"
}()
result := <-ch
fmt.Println("获取到的结果:", result)
}
2. 半同步模式
半同步模式是将异步和同步结合起来,通常用于需要确认任务完成的场景。使用通道来阻塞等待结果。
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
time.Sleep(2 * time.Second)
ch <- "结果"
}()
result := <-ch
fmt.Println("获取到的结果:", result)
}
3. 活动对象模式
活动对象模式是将一个对象的状态与其操作分离的一种模式,允许多个线程对该对象进行操作。通过使用通道来实现并发。
package main
import (
"fmt"
)
type ActiveObject struct {
ch chan string
}
func NewActiveObject() *ActiveObject {
ao := &ActiveObject{ch: make(chan string)}
go ao.run()
return ao
}
func (ao *ActiveObject) run() {
for msg := range ao.ch {
fmt.Println("处理消息:", msg)
}
}
func (ao *ActiveObject) Send(msg string) {
ao.ch <- msg
}
func main() {
ao := NewActiveObject()
ao.Send("Hello")
ao.Send("World")
}
4. 断路器模式
断路器模式用于防止应用程序在故障状态下继续请求。它通过检测失败的频率来实现。
package main
import (
"fmt"
"time"
)
type CircuitBreaker struct {
failureCount int
threshold int
state string
}
func NewCircuitBreaker(threshold int) *CircuitBreaker {
return &CircuitBreaker{threshold: threshold, state: "CLOSED"}
}
func (cb *CircuitBreaker) Call() {
if cb.state == "OPEN" {
fmt.Println("断路器处于打开状态,拒绝请求")
return
}
// 模拟调用
err := simulateCall()
if err != nil {
cb.failureCount++
if cb.failureCount >= cb.threshold {
cb.state = "OPEN"
fmt.Println("断路器打开")
}
} else {
cb.failureCount = 0
}
time.Sleep(1 * time.Second)
}
func simulateCall() error {
// 这里可以模拟错误情况
return fmt.Errorf("调用失败")
}
func main() {
cb := NewCircuitBreaker(3)
for i := 0; i < 10; i++ {
cb.Call()
}
}
5. 超时模式
超时模式用于防止操作长时间阻塞,可以通过 context
包来实现。
package main
import (
"context"
"fmt"
"time"
)
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
select {
case result := <-doWork(ctx):
fmt.Println("获取到的结果:", result)
case <-ctx.Done():
fmt.Println("操作超时")
}
}
func doWork(ctx context.Context) <-chan string {
ch := make(chan string)
go func() {
time.Sleep(3 * time.Second) // 模拟长时间操作
ch <- "结果"
}()
return ch
}
6. 回避模式
回避模式通过在出现问题时采取备用方案,以避免影响整体流程。
package main
import (
"fmt"
)
func main() {
result := safeCall()
fmt.Println("获取到的结果:", result)
}
func safeCall() string {
defer func() {
if r := recover(); r != nil {
fmt.Println("发生错误:", r)
}
}()
// 模拟错误
return riskyOperation()
}
func riskyOperation() string {
panic("发生严重错误")
}
7. 双重检查锁
双重检查锁定用于确保单例模式的线程安全。
package main
import (
"fmt"
"sync"
)
type Singleton struct{}
var instance *Singleton
var mu sync.Mutex
func GetInstance() *Singleton {
if instance == nil {
mu.Lock()
defer mu.Unlock()
if instance == nil {
instance = &Singleton{}
}
}
return instance
}
func main() {
s1 := GetInstance()
s2 := GetInstance()
fmt.Println(s1 == s2) // true
}
8. 保护式挂起
保护式挂起用于在多个goroutine中安全地等待某个条件的发生。
package main
import (
"fmt"
"sync"
"time"
)
var wg sync.WaitGroup
func main() {
wg.Add(1)
go func() {
defer wg.Done()
time.Sleep(2 * time.Second)
fmt.Println("操作完成")
}()
wg.Wait() // 等待所有操作完成
fmt.Println("所有操作已完成")
}
9. 核反应模式
核反应模式用于管理共享资源的并发访问。
package main
import (
"fmt"
"sync"
"time"
)
type Reactor struct {
mu sync.Mutex
}
func (r *Reactor) Access() {
r.mu.Lock()
defer r.mu.Unlock()
// 模拟处理
time.Sleep(1 * time.Second)
fmt.Println("资源被访问")
}
func main() {
r := &Reactor{}
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
r.Access()
}()
}
wg.Wait() // 等待所有操作完成
}
10. 调度器模式
调度器模式用于管理多个任务的执行,通常与工作池一起使用。
package main
import (
"fmt"
"sync"
)
func main() {
const numWorkers = 3
jobs := make(chan int, 10)
var wg sync.WaitGroup
// 启动工作池
for w := 1; w <= numWorkers; w++ {
wg.Add(1)
go func(worker int) {
defer wg.Done()
for job := range jobs {
fmt.Printf("工人 %d 处理任务 %d\n", worker, job)
}
}(w)
}
// 添加任务
for j := 1; j <= 10; j++ {
jobs <- j
}
close(jobs) // 关闭任务通道
wg.Wait() // 等待所有工作完成
}
11. 反应器模式
反应器模式用于处理异步事件,并在事件到达时进行处理。
package main
import (
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("错误:", err)
return
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("错误:", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
fmt.Fprintln(conn, "你好,连接成功!")
}
12. Proactor/Per-CPU
Proactor模式适用于需要处理大量异步事件的场景,通常用于网络应用。
package main
import (
"fmt"
"net"
)
func main() {
listener, err := net.Listen("tcp", ":8080")
if err != nil {
fmt.Println("错误:", err)
return
}
defer listener.Close()
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("错误:", err)
continue
}
go handleConnection(conn)
}
}
func handleConnection(conn net.Conn) {
defer conn.Close()
fmt.Fprintln(conn, "你好,连接成功!")
}
13. 多进程模式
多进程模式通常用于需要进行大规模计算的场景,可以使用Go的并发特性实现。
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("go", "version")
output, err := cmd.Output()
if err != nil {
fmt.Println("错误:", err)
return
}
fmt.Println(string(output))
}
Go语言的并发编程为开发者提供了强大的工具来构建高效的网络应用。本文探讨了多个并发模式以及调度器的基本概念,并提供了详细的代码示例及注释。希望这些内容能够帮助你更深入地理解Go的并发模型及其应用场景。在实际开发中,根据不同的需求选择合适的并发模式,将有助于提高代码的可读性和执行效率。
版权声明:本文为原创文章,版权归 全栈开发技术博客 所有。
本文链接:https://www.lvtao.net/dev/go-goroutine.html
转载时须注明出处及本声明