Golang fmt标准库 Golang

package main

import "fmt"
import "time"

type User struct {
    Name  string
    Email string
    Age   int
}

func main(){

    // fmt包介绍

    // fmt 是一个用于格式化 I/O 的标准库包,提供了格式化字符串输出和输入的功能。
    // fmt 包广泛用于打印日志、调试信息以及格式化字符串等场景

    // 核心功能分类
    //  格式化输出函数

    //  Print 系列:直接输出到标准输出。
    //  Fprint 系列:输出到指定的 io.Writer。
    //  Sprint 系列:将结果作为字符串返回。
    //  格式化输入函数

    //  Scan 系列:从标准输入读取数据。
    //  Fscan 系列:从指定的 io.Reader 读取数据。
    //  Sscan 系列:从字符串中解析数据。
    //  格式化字符串

    //  使用格式化占位符实现自定义输出。

    // 输出函数
    // fmt.Print    直接输出内容,不自动换行。
    // fmt.Println  输出内容,并在结尾自动换行。
    // fmt.Printf   支持格式化输出,需提供格式化字符串(如 %d, %s)。
    // fmt.Sprint   格式化内容并返回字符串,不输出。
    // fmt.Sprintln 格式化内容,添加换行符后返回字符串。
    // fmt.Sprintf  格式化内容并返回字符串,不输出。

    name := "Alice"
    age := 30
    fmt.Print("Hello, ") // 不换行
    fmt.Println("world!") // 换行
    fmt.Printf("My name is %s and I am %d years old.\n", name, age)

    result := fmt.Sprintf("Name: %s, Age: %d", name, age) // 返回字符串
    fmt.Println(result)

    // 输出复杂数据
    user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
    fmt.Printf("Default: %v\n", user)
    fmt.Printf("With field names: %+v\n", user)
    fmt.Printf("Go syntax: %#v\n", user)

    // 输出时间
    fmt.Printf("Current time: %s\n", time.Now().Format("2006-01-02 15:04:05"))

    // 输入函数
    // fmt.Scan 从标准输入读取值,并赋值给提供的变量。
    // fmt.Scanln   类似 Scan,但要求输入换行结束。
    // fmt.Scanf    从标准输入按格式化字符串读取值。
    // fmt.Fscan    从 io.Reader 中读取值,并赋值给提供的变量。
    // fmt.Sscan    从字符串中解析值,并赋值给提供的变量。

    var name1 string
    var age1 int
    fmt.Println("Enter your name and age:")
    fmt.Scanln(&name1, &age1) // 从标准输入读取数据
    fmt.Printf("Hello %s, you are %d years old.\n", name1, age1)

    // 通用占位符
    // %v   默认格式输出变量的值。
    // %+v  输出结构体时,会包含字段名和值。
    // %#v  输出变量的 Go 语法表示形式。
    // %T   输出变量的类型。
    // %%   输出 % 字符。

    // 数字相关占位符
    // %b   二进制表示。
    // %c   对应的 Unicode 字符。
    // %d   十进制表示。
    // %o   八进制表示。
    // %x   十六进制表示(小写字母)。
    // %X   十六进制表示(大写字母)。
    // %f   十进制浮点数表示(默认精度 6)。
    // %e   科学计数法表示(小写 e)。
    // %E   科学计数法表示(大写 E)。

    // 字符串相关占位符
    // %s   输出字符串或字节切片内容。
    // %q   带双引号的字符串。
    // %x   每个字节以两位十六进制表示(小写)。
    // %X   每个字节以两位十六进制表示(大写)。

    // %t   输出布尔值的 true 或 false。

}

杨佳乐 发布于  2025-1-10 19:45 

Golang 并发编程sync包 Golang

package main

import (
    "fmt"
    "sync"
)

// sync包
func main(){

    // Go 语言的 sync 包提供了用于同步的原语,主要用于多个 goroutine 之间的协调与同步。
    // sync 包中有几个非常重要的同步工具,帮助你处理并发编程中的竞态条件、资源共享和执行顺序等问题

    // sync.WaitGroup
    // sync.WaitGroup 用于等待一组 goroutine 完成。它可以帮助你在多个 goroutine 执行完之后再继续执行后续的代码

    // 用法:
    // 调用 Add(n) 来设置等待的 goroutine 数量。
    // 每个 goroutine 执行完后,调用 Done() 来通知 WaitGroup,表示一个 goroutine 完成了。
    // Wait() 会阻塞,直到所有的 goroutine 都完成。

    //示例
    // func task(id int, wg *sync.WaitGroup) {
    //  defer wg.Done() // 标记 goroutine 完成
    //  fmt.Printf("任务 %d 开始\n", id)
    // }

    // func main() {
    //  var wg sync.WaitGroup

    //  // 启动多个 goroutine
    //  for i := 1; i <= 3; i++ {
    //      wg.Add(1) // 设置等待的 goroutine 数量
    //      go task(i, &wg)
    //  }

    //  wg.Wait() // 等待所有 goroutine 完成
    //  fmt.Println("所有任务完成")
    // }

    // sync.Mutex
    // 互斥锁,用于保护临界区,确保同一时刻只有一个 goroutine 可以访问共享资源。它可以防止多个 goroutine 同时访问共享数据,从而避免数据竞态问题
    // 用法:
    // Lock():获取锁,如果锁已经被其他 goroutine 持有,当前 goroutine 会被阻塞,直到锁被释放。
    // Unlock():释放锁,允许其他 goroutine 获取锁。

    // var (
    //  mu    sync.Mutex
    //  count int
    // )
    // func increment(wg *sync.WaitGroup) {
    //  defer wg.Done()
    //  mu.Lock()         // 获取锁
    //  count++           // 访问共享数据
    //  mu.Unlock()       // 释放锁
    // }

    // func main() {
    //  var wg sync.WaitGroup

    //  // 启动多个 goroutine 来并发地增加 count
    //  for i := 0; i < 5; i++ {
    //      wg.Add(1)
    //      go increment(&wg)
    //  }

    //  wg.Wait()
    //  fmt.Println("最终 count 值:", count)
    // }

    // sync.RWMutex
    // 读写锁,它允许多个 goroutine 同时读取共享数据,但写入操作会阻塞所有其他读写操作。适用于读多写少的场景
    // 用法:
    // RLock():获取读锁,多个 goroutine 可以同时获取读锁。
    // RUnlock():释放读锁。
    // Lock():获取写锁,写锁会阻塞所有的读锁和写锁。
    // Unlock():释放写锁。

    // var (
    //  rwmu   sync.RWMutex
    //  data   int
    // )

    // func readData(wg *sync.WaitGroup) {
    //  defer wg.Done()
    //  rwmu.RLock()       // 获取读锁
    //  fmt.Println("读取数据:", data)
    //  rwmu.RUnlock()     // 释放读锁
    // }

    // func writeData(wg *sync.WaitGroup, value int) {
    //  defer wg.Done()
    //  rwmu.Lock()        // 获取写锁
    //  data = value
    //  fmt.Println("写入数据:", data)
    //  rwmu.Unlock()      // 释放写锁
    // }

    // func main() {
    //  var wg sync.WaitGroup

    //  // 启动多个 goroutine
    //  wg.Add(3)
    //  go readData(&wg)
    //  go writeData(&wg, 42)
    //  go readData(&wg)

    //  wg.Wait()
    // }

    // sync.Once
    // 用于确保某个操作只执行一次,常用于初始化操作。无论多少次调用 Do(),该操作只会执行一次
    // 用法:
    // Do(f func()):执行传入的函数 f,如果该函数尚未执行过,它会执行一次。

    // var once sync.Once

    // func initOnce() {
    //  fmt.Println("初始化一次操作")
    // }

    // func main() {
    //  // 调用 Do,确保初始化操作只执行一次
    //  once.Do(initOnce)
    //  once.Do(initOnce) // 不会执行
    //  once.Do(initOnce) // 不会执行
    // }

    // sync/atomic 包
    // 提供了原子操作,用于在并发环境中安全地操作基本数据类型。原子操作是不可分割的操作,保证在执行过程中不会被其他 goroutine 中断
    // 常用函数:
    // atomic.AddInt32()
    // atomic.CompareAndSwapInt32()
    // atomic.LoadInt32()
    // atomic.StoreInt32()

    // import (
    //  "fmt"
    //  "sync"
    //  "sync/atomic"
    // )

    // var counter int32

    // func increment(wg *sync.WaitGroup) {
    //  defer wg.Done()
    //  atomic.AddInt32(&counter, 1) // 原子加操作
    // }

    // func main() {
    //  var wg sync.WaitGroup

    //  // 启动多个 goroutine
    //  for i := 0; i < 5; i++ {
    //      wg.Add(1)
    //      go increment(&wg)
    //  }

    //  wg.Wait()
    //  fmt.Println("最终计数:", counter)
    // }

    // 总结:
    // sync 包提供了强大的同步工具,帮助开发者处理并发编程中的数据共享、任务同步和并发安全问题。常用的同步工具有:
    // sync.WaitGroup:等待多个 goroutine 完成。
    // sync.Mutex:互斥锁,保证同一时刻只有一个 goroutine 访问共享资源。
    // sync.RWMutex:读写锁,支持多个 goroutine 同时读取,但写入时会阻塞所有读写操作。
    // sync.Once:确保某个操作只执行一次,适用于初始化操作。
    // sync/atomic:原子操作,保证在并发环境下的数据操作安全。
}

杨佳乐 发布于  2025-1-10 18:27 

Golang 并发编程Demo2 模拟多消费者队列 Golang

package main

import (
    "fmt"
    "sync"
    //"time"
)

//模拟多个工作者与任务队列

//模拟了一个工作池,其中有多个工作者处理多个任务。通过 select,每个工作者可以从任务队列中接收任务并处理。

// 工作者消费方法
func worker(id int,jobs <-chan string,results chan<- string,wg *sync.WaitGroup){
    // jobs 为待消费队列,所以这里只需要读取
    // results 为消费后结果,这里只需要写入

    //消费后执行一次任务完成
    defer wg.Done()

    for job := range jobs {
        //模拟处理工作

        //工作处理完成
        results <- fmt.Sprintf("工作者 %d 完成任务, %s",id,job)
    }
}

func main(){

    var wg sync.WaitGroup

    // 创建任务队列
    jobs := make(chan string,5)
    //创建结果队列
    results := make(chan string,5)

    // 启动worker
    for i := 0; i<=3; i++{
        wg.Add(1)
        go worker(i,jobs,results,&wg)
    }

    //向队列中添加任务

    for i := 1; i<=5; i++ {
        jobs <- fmt.Sprintf("任务 %d",i)
    }

    close(jobs) // 关闭任务队列,表示没有更多任务

    go func(){
        wg.Wait()
        close(results) // 执行完毕,关闭结果通道
    }()

    // 打印结果
    for result := range results{
        fmt.Println(result)
    }

}

杨佳乐 发布于  2025-1-10 16:54 

Golang 并发编程Demo1 Golang

package main

import "fmt"
import "sync"

// 并发demo1
// 假如我正在写一个响应用户信息的接口,这个接口需要获取用户基本信息、余额明细这两个比较耗时的操作
// 此时可以通过goroutine开启两个线程,同时处理获取这两个数据的操作,都获取完了以后组合数据响应
func main(){
    fmt.Println(getUserData())
}

// 定义一个用户数据结构体
type UserData struct{
    BaseInfo string
    BalanceInfo string
}

// 获取用户基本信息函数
func getBaseInfo(ch chan<- string){
    ch <- "张三"
}

//获取用户余额信息函数
func getBalanceInfo(ch chan<- string){
    ch <- "余额"
}

func getUserData() UserData {
    // 定义一个WaitGroup 等待所有任务执行完成
    var wg sync.WaitGroup

    ch1 := make(chan string)
    ch2 := make(chan string)

    wg.Add(1)
    go func(){
        defer wg.Done()
        getBaseInfo(ch1)
    }()

    wg.Add(1)
    go func(){
        defer wg.Done()
        getBalanceInfo(ch2)
    }()

    udata := UserData{
        BaseInfo:  <-ch1,
        BalanceInfo: <-ch2,
    }

    //wg.Wait 需要放在接收数据后面,否则会导致死锁,因为wg Wait是阻塞的,放在后面,没有接收通道数据
    wg.Wait()  

    return udata
}

杨佳乐 发布于  2025-1-10 15:10 

Golang 并发编程goroutine Golang

package main

import "fmt"

// 学习并发前应了解的一些基本内容

// 操作系统基础:进程与线程
// 进程(Process):一个进程是操作系统中正在执行的程序。每个进程都有自己的内存空间、代码和数据。进程是操作系统进行资源管理的基本单位。
// 线程(Thread):线程是进程中的一个执行单元,一个进程可以有多个线程。线程共享进程的资源,但每个线程有自己的执行栈。线程是操作系统调度的基本单位。
// 多线程和多进程:多进程是指操作系统同时运行多个独立的进程,而多线程是指在一个进程内部运行多个线程。Go 中的 goroutines 是一种轻量级线程,与操作系统线程类似但更轻量。
// 上下文切换:操作系统会根据一定的调度算法,暂停当前进程或线程的执行,并切换到另一个进程或线程的执行,这个过程称为上下文切换。

// 并发和并行
// 并发(Concurrency):并发指的是程序中有多个任务正在进行,但是它们可能并不同时执行。不同任务之间通过切换执行来模拟“同时”进行。
// 并行(Parallelism):并行指的是多个任务真正同时执行。并行通常发生在多核处理器上,多个任务在不同的 CPU 核心上同时执行。
// 你可以将 并发 想象成同时处理多个任务,但每次只做其中一个任务,通过快速切换来产生“同时”的效果。Go 的 goroutine 是一种并发模型,它可以非常高效地处理成千上万的任务。

// 并发编程
func main(){

    // Go 的并发编程非常强大,它通过 goroutines 和 channels 来帮助开发者轻松处理并发任务。
    // Go 的并发模型基于 CSP(Communicating Sequential Processes)理论,通过通信而不是共享内存来处理并发。

    // Goroutines
    // Go 中的并发单元是 goroutine,它是一个轻量级的线程。goroutine 通过 go 关键字启动,Go 的调度器会自动管理多个 goroutine 的执行

    // 启动一个新的 goroutine
    go printMessage()

    // 主线程等待 goroutine 完成
    // fmt.Scanln()

    // go printMessage() 启动了一个新的 goroutine,这个函数会并发执行。

    // 主函数 fmt.Scanln() 用于阻塞,等待用户输入,确保 goroutine 可以有足够的时间执行。

    // Channels
    // Channel 是 Go 提供的一个强大的并发原语,允许 goroutines 之间进行数据传递。Go 中的 Channel 可以是有缓冲的或无缓冲的。

    // 无缓冲 Channel
    // 无缓冲 Channel 是一种同步的通信方式,当一个 goroutine 向 Channel 发送数据时,另一个 goroutine 必须接收这个数据才能继续。

    ch := make(chan int)

    go sender(ch)

    // 接收数据并打印
    data := <-ch
    fmt.Println("Received data:", data)

    // 有缓冲Channel
    // 有缓冲 Channel 允许多个数据被发送到 Channel 中,而不需要立即被接收。它允许一定数量的缓冲区

    chs := make(chan int, 3) // 创建一个缓冲区为 3 的 Channel

    go senders(chs)

    for i := 0; i < 5; i++ {
        datas := <-chs
        fmt.Println("Received:", datas)
    }
    //缓冲 Channel ch := make(chan int, 3) 允许最多存储 3 个整数。
    //发送者可以发送多个数据而不需要立即被接收。

    // 关闭Channel
    // 当所有的发送操作完成后,可以关闭 Channel。关闭 Channel 使得接收者能够知道不再有更多数据。
    // func sender(ch chan int) {
    //  for i := 0; i < 3; i++ {
    //      ch <- i
    //  }
    //  close(ch) // 关闭 Channel
    // }

    // func main() {
    //  ch := make(chan int)

    //  go sender(ch)

    //  for data := range ch {
    //      fmt.Println("Received:", data)
    //  }
    // }
    // close(ch) 关闭 Channel,表示不再有数据发送到该 Channel。
    // 使用 for range 循环来接收数据,直到 Channel 被关闭

    // Select 语句
    // select 语句允许你等待多个 Channel 的操作。它会选择一个可用的 Channel 操作执行

    // func sender(ch chan int) {
    //  ch <- 42
    // }

    // func main() {
    //  ch1 := make(chan int)
    //  ch2 := make(chan int)

    //  go sender(ch1)
    //  go sender(ch2)

    //  select {
    //  case data := <-ch1:
    //      fmt.Println("Received from ch1:", data)
    //  case data := <-ch2:
    //      fmt.Println("Received from ch2:", data)
    //  }
    // }

    //select 等待多个 Channel 中的一个操作完成,执行对应的 case 语句。
    //这个例子中,select 会从 ch1 或 ch2 中接收数据,并执行相应的打印操作。

    // WaitGroup
    // WaitGroup 用于等待一组 goroutine 执行完成。它通常用于等待并发任务完成
    // import (
    //  "fmt"
    //  "sync"
    // )

    // func task(id int, wg *sync.WaitGroup) {
    //  defer wg.Done() // 告诉 WaitGroup 任务已完成
    //  fmt.Println("Task", id, "started")
    // }

    // func main() {
    //  var wg sync.WaitGroup

    //  for i := 1; i <= 5; i++ {
    //      wg.Add(1) // 每启动一个 goroutine,就增加计数
    //      go task(i, &wg)
    //  }

    //  wg.Wait() // 等待所有 goroutines 完成
    //  fmt.Println("All tasks finished.")
    // }
    // wg.Add(1) 增加等待计数,每启动一个 goroutine 都调用。
    // defer wg.Done() 在每个任务完成时减少计数。
    // wg.Wait() 等待所有任务完成。

    // Worker Pool 模式
    // 在实际应用中,常常需要将任务分配给多个 worker goroutine 来处理,这就是 worker pool 模式

    // import (
    //  "fmt"
    //  "sync"
    // )

    // func worker(id int, ch chan int, wg *sync.WaitGroup) {
    //  defer wg.Done()
    //  for task := range ch {
    //      fmt.Printf("Worker %d processing task %d\n", id, task)
    //  }
    // }

    // func main() {
    //  var wg sync.WaitGroup
    //  ch := make(chan int, 10)

    //  // 启动 3 个 worker
    //  for i := 1; i <= 3; i++ {
    //      wg.Add(1)
    //      go worker(i, ch, &wg)
    //  }

    //  // 发送任务
    //  for task := 1; task <= 10; task++ {
    //      ch <- task
    //  }

    //  close(ch) // 关闭 Channel,表示没有更多任务
    //  wg.Wait()  // 等待所有 worker 完成
    // }

    // 这里有 3 个 worker,每个 worker 从 Channel 中获取任务进行处理。
    // 通过 close(ch) 关闭 Channel 来通知 worker 没有更多任务。

    // 并发调试

    // Go 提供了 -race 标志来检测并发代码中的数据竞争问题。你可以使用它来运行程序并查找潜在的并发问题。
    // -race 标志会检测并发访问共享数据时的竞争条件,帮助你找出代码中的潜在问题。

    // 总结
    // Go 的并发编程非常强大,核心原语是 goroutines 和 channels。你可以通过以下步骤提升并发编程能力:

    // 理解 goroutines 和它们如何启动。
    // 学习如何通过 channels 在 goroutines 之间传递数据。
    // 使用 select 语句在多个 channels 之间进行选择。
    // 利用 WaitGroup 来同步多个 goroutines。
    // 掌握 worker pool 模式来处理并发任务。
    // 调试并发代码,确保没有数据竞争问题。

}

func senders(ch chan int) {
    for i := 0; i < 5; i++ {
        ch <- i
        fmt.Println("Sent:", i)
    }
}

func sender(ch chan int) {
    ch <- 42
    fmt.Println("Sent data")
}

func printMessage(){
    fmt.Println("Hello from Goroutines")
}

// 更通俗简单的理解go 并发
// 什么是并发?
// 想象一下,你正在做一份工作报告,而你有两个任务:

// 任务 1:查找资料。
// 任务 2:整理内容。
// 在传统的工作模式下,你可能会先做任务 1,然后再做任务 2,这叫做 顺序执行。但假设你现在有两个人,你可以让一个人做任务 1,另一个人做任务 2,这样你可以 并行 完成这两个任务。

// 并发 是指我们有多个任务,它们可能并不会真正同时执行,但它们的执行是交替进行的,就像你在忙碌的时刻,切换任务一样。并行则是任务在不同的 CPU 核心上同时执行。

// 在 Go 中,虽然我们通常是通过 goroutine 来实现并发,但很多时候我们只是希望它们看起来像是“同时”运行的。

// 什么是 Goroutine?
// Goroutine 是 Go 中的轻量级线程,它和操作系统的线程不一样。你可以把 goroutine 想象成你在做任务时的“临时助手”。你给它一个任务,它就开始工作。

// 启动一个 goroutine:通过 go 关键字,你可以启动一个新的 goroutine。每个 goroutine 都是并发执行的。
// 举例
// func sayHello() {
//     fmt.Println("Hello, Goroutine!")
// }

// func main() {
//     // 启动一个新的 goroutine
//     go sayHello()

//     // 主线程继续执行
//     fmt.Println("Hello from main!")
// }

// 这里,go sayHello() 启动了一个新的 goroutine,它会和 main 函数一起并发执行。你会看到输出顺序是随机的,因为 sayHello 和 main 是并发运行的。

// Goroutine 是如何工作的?
// Go 的 goroutine 非常轻量,启动一个 goroutine 的成本非常低,可能只需要几 KB 的内存。
// 它们由 Go 运行时调度和管理,你不需要显式地创建或销毁 goroutine。
// 你可以理解为:在 Go 中,启动一个新的任务(goroutine)就像给你指派一个新的助手,而你无需关心每个助手的具体管理细节。

// 什么是 Channel?
// Channel 是 Go 中用于在 goroutines 之间传递信息的通道。你可以把它理解为“邮递员”,它负责把消息从一个 goroutine 传递到另一个 goroutine。

// 假设你有两个任务:

// 任务 1:从一堆数字中筛选出偶数。
// 任务 2:将筛选出的偶数做一些加法处理。
// 你不想让任务 1 和任务 2 直接互相操作,而是希望它们通过传递信息的方式进行合作。

// 示例
// func filterEven(numbers []int, ch chan int) {
//     for _, num := range numbers {
//         if num%2 == 0 {
//             ch <- num // 发送偶数到 channel
//         }
//     }
//     close(ch) // 关闭 channel,表示任务结束
// }

// func addNumbers(ch chan int) {
//     for num := range ch {
//         fmt.Println("Adding number:", num+1)
//     }
// }

// func main() {
//     numbers := []int{1, 2, 3, 4, 5, 6}
//     ch := make(chan int) // 创建一个 channel

//     // 启动两个 goroutine
//     go filterEven(numbers, ch)
//     go addNumbers(ch)

//     // 主线程等待 goroutines 执行完成
//     fmt.Scanln()
// }

// filterEven 任务会筛选出偶数,并通过 ch <- num 把偶数发送到 channel。
// addNumbers 任务会从 ch 中接收偶数,并在其基础上加 1 后打印出来。
// Channel 在这里充当了一个 消息传递系统,确保两个 goroutine 之间的信息能够正确传递。

// Go 中的并发调度:如何协作?
// Go 的调度器会将多个 goroutine 安排到有限的 CPU 核心上执行。Go 的并发执行并不意味着所有 goroutines 会真正“同时”运行。实际上,Go 会在不同的时刻给 goroutine 分配执行的时间片,快速切换它们的执行。你可以理解为:在一个 CPU 核心上,Go 的调度器会以非常高的速度在多个 goroutines 之间切换任务。

// 核心概念:Go 的并发调度是通过 抢占式调度 来管理的,而不像传统操作系统中那样使用重量级线程。

// 如何用 Select 同时处理多个 Channel?
// 假设你有多个任务需要同时处理,但它们的数据需要通过不同的 channel 传递。Go 提供了 select 语句,它可以让你同时监听多个 channel,哪个 channel 有数据,它就会处理哪个。

// func sender(ch chan string) {
//     ch <- "Hello from sender!"
// }

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

//     go sender(ch1)
//     go sender(ch2)

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

// 这里,select 语句会同时监听 ch1 和 ch2,哪个 channel 先有数据,它就会接收并执行对应的 case。

// 注意,select 可以立即为一次性的,一个select只会执行一次case ,比如同时AB两个任务,select 监听AB,A收到数据select中case会执行,B收到数据不会执行case

// 总结
// 并发编程的核心思想就是让多个任务并行或者交替地执行。Go 中通过 goroutine 来实现并发,通过 channel 在 goroutines 之间传递数据。这些机制让 Go 编程语言非常适合进行并发编程。理解并发的关键是:

// goroutine:让任务并发执行。
// channel:让 goroutines 之间进行通信。
// select:处理多个 channel 操作。

杨佳乐 发布于  2025-1-10 15:09