当前位置: 首页 > article >正文

go并发模型的详细介绍

Go 语言的并发模型是其一大亮点,它使得并发编程变得简单高效。Go 语言并发模型的核心概念是 goroutineschannels。在理解这两个概念之前,我们首先了解并发编程的一些基本概念。

1. 并发与并行

  • 并发(Concurrency):并发是指系统能够在同一时间段内处理多个任务。并发并不意味着多个任务同时执行,而是指在一个时间段内,任务的切换和处理可以交替进行。并发通常是通过时间分片实现的(比如操作系统的时间片轮转)。

  • 并行(Parallelism):并行是指多个任务同时在不同的处理器或核上执行。并行要求硬件支持多核心或者多处理器。

Go 语言的并发模型关注的是 并发,通过调度和轻量级的线程(即 goroutine)实现高效的并发执行。

2. Goroutines

Goroutine 是 Go 中并发执行的基本单元。它是一个由 Go 运行时管理的轻量级线程。相较于操作系统线程,goroutine 的开销非常小,启动一个 goroutine 只需要几个 KB 的内存,因此可以同时创建成千上万个 goroutine。

启动 Goroutine

使用 go 关键字启动一个 goroutine。goroutine 会在后台并发执行一个函数。

package main

import (
    "fmt"
    "time"
)

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

func main() {
    go sayHello()  // 启动一个 goroutine
    time.Sleep(1 * time.Second)  // 等待 goroutine 执行完毕
    fmt.Println("Hello from main")
}

在这个例子中,go sayHello() 启动了一个新的 goroutine。由于主函数没有等待 goroutine 执行完,因此我们加了 time.Sleep 来确保主函数能等待 goroutine 执行完成。

Goroutine 特点:
  • 轻量级:启动一个 goroutine 的开销很小。
  • 由 Go 运行时管理:Go 运行时会自动调度 goroutine,可以在一个线程上调度数以千计的 goroutine。
  • 栈动态增长:每个 goroutine 的栈大小非常小,通常是 2 KB。随着需要,栈会自动增长。
  • 非阻塞:如果 goroutine 执行的代码块遇到阻塞,它会被调度到其他的 goroutine,直到阻塞解决。
Goroutine 例子
package main

import "fmt"

func printMessage(msg string) {
    fmt.Println(msg)
}

func main() {
    go printMessage("Hello from Goroutine 1")
    go printMessage("Hello from Goroutine 2")
    fmt.Println("Hello from main")
}

这里启动了两个 goroutine,它们与主函数并发执行,输出的顺序不固定,取决于调度器的执行。

3. Channel

Go 语言的并发模型不仅仅依赖于 goroutines,还依赖于 channels。channels 是一种数据结构,它允许 goroutine 之间进行通信和同步。

Channel 的基本使用

可以通过 make(chan Type) 创建一个 channel。Go 中的 channel 是类型安全的,即它只能传递指定类型的数据。

package main

import "fmt"

func greet(ch chan string) {
    ch <- "Hello from Goroutine"
}

func main() {
    ch := make(chan string)  // 创建一个 channel
    go greet(ch)  // 启动一个 goroutine,传递数据到 channel
    msg := <-ch  // 从 channel 中接收数据
    fmt.Println(msg)
}

在这个例子中:

  • ch <- "Hello from Goroutine":将数据发送到 channel。
  • msg := <-ch:从 channel 中接收数据。
Channel 的特性:
  • 同步:channel 的发送和接收操作是阻塞的,直到数据被发送或接收。
  • 类型安全:channel 必须指定类型,不能跨类型传递数据。
  • 双向和单向:channel 可以作为双向通信的通道,也可以指定为只发送(chan<-)或只接收(<-chan)。
Buffered Channel(缓冲区)

Go 的 channel 不一定需要是无缓冲的。你可以创建带缓冲区的 channel,这样可以在 channel 没有接收者的情况下继续发送数据,直到缓冲区满。

ch := make(chan string, 2)  // 创建一个缓冲区大小为 2 的 channel
ch <- "Hello"
ch <- "Go"
fmt.Println(<-ch)
fmt.Println(<-ch)

带缓冲区的 channel 不会立即阻塞发送方,直到缓冲区满才会阻塞。

4. Goroutines 与 Channels 协作

Goroutines 和 Channels 配合使用时,可以实现非常高效的并发程序设计。通过 channels,goroutines 可以安全地交换数据和同步执行。

示例:生产者-消费者模式

生产者消费者模式是并发编程中常见的设计模式,其中生产者负责生成数据,消费者负责处理数据。

package main

import "fmt"

func producer(ch chan int) {
    for i := 1; i <= 5; i++ {
        ch <- i  // 将数据发送到 channel
    }
    close(ch)  // 关闭 channel,表示生产者不再发送数据
}

func consumer(ch chan int) {
    for num := range ch {  // 从 channel 接收数据,直到 channel 被关闭
        fmt.Println("Consumed:", num)
    }
}

func main() {
    ch := make(chan int)
    go producer(ch)  // 启动生产者
    go consumer(ch)  // 启动消费者
    fmt.Println("Main goroutine")
}

在这个例子中:

  • 生产者 goroutine 通过 ch <- i 向 channel 发送数据。
  • 消费者 goroutine 通过 num := range ch 从 channel 中接收数据。由于 channel 被关闭,消费者会停止接收。

5. Select 语句

select 是 Go 中用于多路复用的语句,可以同时等待多个 channel 操作的完成。它类似于传统编程语言中的 selectpoll

示例:使用 select 处理多个 channel
package main

import "fmt"

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

    go func() { ch1 <- "Hello from ch1" }()
    go func() { ch2 <- "Hello from ch2" }()

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

在这个例子中,select 会阻塞并等待任一 channel 中的数据。如果 ch1ch2 有数据,它会选择接收并执行相应的代码块。

6. 并发模式

Go 的并发模型支持多种并发模式,常见的并发模式包括:

  • 工作池模式(Worker Pool):将任务分配给多个 goroutine 进行处理,任务和工作者之间通过 channel 进行通信。
  • 生产者消费者模式(Producer-Consumer):生产者和消费者通过 channel 进行数据交换。
  • Fan-out, Fan-in:多个 goroutine 从同一个 channel 中读取数据(fan-out),或者多个 goroutine 向同一个 channel 发送数据(fan-in)。

7. 并发安全

Go 提供了多种方式来保证并发程序的安全性:

  • Mutex(互斥锁):可以通过 sync.Mutex 来保护共享资源。
  • Atomic 操作sync/atomic 包提供了原子操作来保证并发安全。
  • Channel:Channel 本身是并发安全的,它可以在 goroutine 之间安全地传递数据。

总结

Go 的并发模型是基于 goroutineschannels 构建的,具备高效、简单、易用的特点。通过 goroutine,Go 可以轻松实现并发执行,而通过 channel,它可以安全地在 goroutines 之间传递数据和进行同步。Go 的并发编程模型大大简化了并发程序的开发,减少了传统并发编程中常见的错误和复杂性,使得开发者可以集中精力解决实际业务问题。


http://www.kler.cn/a/454075.html

相关文章:

  • HDFS与HBase有什么关系?
  • RAGFlow 基于深度文档理解构建的开源 RAG引擎 vm.max_map_count配置
  • vscode搭建C/C++环境
  • 利用OnnxRuntime进行torch模型部署(C++版)——以分类网络为例
  • python通过正则匹配SQL
  • 【每日学点鸿蒙知识】线程创建、构造函数中创建变量仍报错、List上下拖拽,调用JS代码、无法选择本地csr文件问题
  • 修改vue-element-admin,如何连接我们的后端
  • JavaScript 中的对象方法
  • 人工智能与云计算的结合:如何释放数据的无限潜力?
  • Mono里运行C#脚本4—mono_mutex_t 锁的实现
  • VSCode/Visual Studio Code实现点击方法名跳转到具体方法的
  • C# .Net Web 路由相关配置
  • Android学习19 -- NDK4--共享内存(TODO)
  • 机器学习常用评估Metric(ACC、AUC、ROC)
  • 自動提取API爬蟲代理怎麼實現?
  • Docker环境下数据库持久化与多实例扩展实践指南
  • 再谈ChatGPT降智:已蔓延到全端,附解决方案!
  • docker怎么复制容器的文件到宿主机
  • 基于Spring Boot的电影售票系统
  • OCR(三)windows 环境基于c++的 paddle ocr 编译【CPU版本】