Go 并发
                           
天天向上
发布: 2025-03-26 00:00:02

原创
274 人浏览过

Go 语言的并发处理是其一大亮点。Go 提供了轻量级的线程称为 goroutine,使得并发编程变得非常简单和高效。Go 的并发模型基于 CSP(通信顺序进程)模型,通过 goroutine 和 channel 来进行协作。

1. Goroutine

Goroutine 是 Go 中的轻量级线程。它比操作系统线程更加高效,每个 goroutine 的内存开销非常小,通常只有几 KB,并且 Go 的调度器会自动管理它们。

1.1 启动 Goroutine

要启动一个 goroutine,只需使用 go 关键字:

package main

import "fmt"

func sayHello() {
    fmt.Println("Hello from Goroutine!")
}

func main() {
    go sayHello() // 启动一个 goroutine
    fmt.Println("Hello from Main!")
}

在上述代码中,sayHello() 函数会作为一个 goroutine 被异步执行,而 main() 函数会继续执行。程序的输出顺序不确定,可能是:

Hello from Main!
Hello from Goroutine!

也可能是:

Hello from Goroutine!
Hello from Main!

1.2 并发执行多个 Goroutine

你可以启动多个 goroutine 来并发执行任务:

package main

import "fmt"

func task(id int) {
    fmt.Printf("Task %d started\n", id)
}

func main() {
    for i := 1; i <= 5; i++ {
        go task(i) // 启动 5 个 goroutine
    }
    fmt.Println("All tasks started")
}

在这个例子中,启动了 5 个 goroutine 执行 task 函数。


2. Channel

Channel 是 Go 中用于在 goroutine 之间进行通信的管道。它是一种强类型的数据结构,可以传递数据,也可以控制 goroutine 之间的同步。通过 channel,多个 goroutine 可以通过发送和接收数据进行协作。

2.1 创建和使用 Channel

你可以使用 make 函数创建一个 channel:

package main

import "fmt"

func main() {
    ch := make(chan string) // 创建一个字符串类型的 channel

    go func() {
        ch <- "Hello from Goroutine!" // 向 channel 发送数据
    }()

    msg := <-ch // 从 channel 接收数据
    fmt.Println(msg)
}

在这个例子中,main 函数创建了一个 channel,并且启动了一个 goroutine 向该 channel 发送数据。主函数从 channel 接收数据并打印。

2.2 关闭 Channel

当你不再需要向 channel 发送数据时,可以关闭 channel。关闭的 channel 无法再发送数据,但是可以继续接收数据,直到 channel 中的所有数据都被接收完。

package main

import "fmt"

func main() {
    ch := make(chan string)

    go func() {
        ch <- "Hello"
        close(ch) // 关闭 channel
    }()

    msg, ok := <-ch
    if !ok {
        fmt.Println("Channel closed!")
    } else {
        fmt.Println(msg)
    }
}

2.3 在多个 goroutine 之间使用 Channel

如果需要让多个 goroutine 之间通过 channel 进行数据交换,可以使用 select 语句。

package main

import "fmt"

func worker(id int, ch chan string) {
    ch <- fmt.Sprintf("Worker %d done", id)
}

func main() {
    ch := make(chan string)
    for i := 1; i <= 3; i++ {
        go worker(i, ch)
    }

    for i := 1; i <= 3; i++ {
        fmt.Println(<-ch) // 从 channel 接收消息
    }
}

在这个例子中,启动了 3 个 goroutine,通过 channel 将工作完成的消息传递回主线程。


3. Select 语句

select 语句可以同时监听多个 channel,选择其中一个 channel 的数据进行操作。它类似于 switch 语句,但是用于 channel 操作。

3.1 select 示例

package main

import "fmt"

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        ch1 <- "From Channel 1"
    }()

    go func() {
        ch2 <- "From Channel 2"
    }()

    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    }
}

在这个例子中,select 会监听 ch1ch2,并打印从其中一个 channel 接收到的消息。

3.2 select 的默认选择

select 语句还有一个 default 分支,当所有 channel 都没有准备好时,default 分支会被执行。

package main

import "fmt"

func main() {
    ch := make(chan string)

    select {
    case msg := <-ch:
        fmt.Println(msg)
    default:
        fmt.Println("No message received!")
    }
}

如果 ch 没有数据,default 会被执行。


4. Goroutine 的同步

Go 提供了多种方式来同步多个 goroutine 的执行。最常见的方式有:

  • WaitGroup:用于等待一组 goroutine 完成。
  • Mutex(互斥锁):用于在多个 goroutine 之间共享资源时保证互斥。

4.1 WaitGroup 示例

sync.WaitGroup 用于等待一组 goroutine 执行完毕。

package main

import (
    "fmt"
    "sync"
)

func task(id int, wg *sync.WaitGroup) {
    defer wg.Done() // 当任务完成时调用 Done
    fmt.Printf("Task %d started\n", id)
}

func main() {
    var wg sync.WaitGroup
    for i := 1; i <= 3; i++ {
        wg.Add(1) // 增加等待的 goroutine 数量
        go task(i, &wg)
    }

    wg.Wait() // 等待所有 goroutine 完成
    fmt.Println("All tasks completed")
}

在这个例子中,sync.WaitGroup 用来确保主程序等待所有 goroutine 执行完毕。

4.2 Mutex 示例

sync.Mutex 用于保护共享资源,确保在同一时间只有一个 goroutine 能访问该资源。

package main

import (
    "fmt"
    "sync"
)

var mu sync.Mutex
var count int

func increment() {
    mu.Lock()         // 锁定
    count++
    mu.Unlock()       // 解锁
}

func main() {
    for i := 0; i < 5; i++ {
        go increment()
    }

    // 等待 goroutine 完成
    fmt.Println("Final count:", count)
}

5. 总结

  • Goroutine:Go 提供了轻量级的线程,能够在后台并发执行任务,极大提高了并发性能。
  • Channel:Go 中 goroutine 之间的通信是通过 channel 完成的,channel 是线程安全的,支持同步和数据传递。
  • Selectselect 语句可以监听多个 channel,当某个 channel 有数据时,执行对应的代码。
  • 同步方式:使用 sync.WaitGroup 等工具来等待多个 goroutine 完成;使用 sync.Mutex 来保证并发访问时的资源安全。

Go 的并发编程模型简单、高效,是进行并发处理的理想选择。通过合理使用 goroutine 和 channel,可以轻松构建并发程序。

发表回复 0

Your email address will not be published. Required fields are marked *