目录

Goroutines

通道(Channel)

代码示例


Goroutines

  1. 定义与特点

    • Goroutines是Go语言中实现并发的基本单位。
    • 它比传统的线程更轻量级,拥有更小的内存占用和更快的启动时间。
    • 在Go程序中,您可以轻松地启动成千上万的Goroutines。
  2. 使用方法

    • 使用go关键字后跟一个函数调用,即可启动一个Goroutine。
    • 例如:go myFunction()
package main

import (
    "fmt"
    "time"
)

// 定义一个简单的函数
func printNumbers(prefix string) {
    for i := 1; i <= 5; i++ {
        fmt.Println(prefix, i)
        // 休眠一段时间,以模拟实际操作中的耗时
        time.Sleep(time.Millisecond * 500)
    }
}

func main() {
    // 使用 go 关键字启动一个新的 Goroutine
    go printNumbers("Goroutine")

    // 主Goroutine也执行相同的函数
    printNumbers("Main")

    // 等待足够长的时间以确保Goroutine完成
    // 注意:这不是同步Goroutines的推荐方式
    // 后续课程将介绍更好的方法(如WaitGroup或Channel)
    time.Sleep(time.Second * 3)

    fmt.Println("主函数执行完毕")
}
  • 在实际应用中,我们通常不使用time.Sleep来等待Goroutines完成,而是使用像通道(Channels)或sync.WaitGroup这样的同步机制。
  • 这个示例的目的是简单地展示Goroutines的并发执行。在后续课程中,我们将探讨更高级的同步技术和并发模式。

 

通道(Channel)

  1. 定义与作用

    • Channel是Go语言中的一种内置类型,用于在Goroutines之间安全地传递数据。
    • 它可以帮助解决Goroutines之间的同步问题。
  2. 类型与使用

    • 有缓冲和无缓冲两种类型的Channel。
    • 创建示例:ch := make(chan int)(无缓冲)或ch := make(chan int, 5)(有缓冲,容量为5)。
    • 数据传递:使用<-操作符向Channel发送或接收数据。

 

package main

import (
    "fmt"
    "time"
)

func main() {
    // 创建一个无缓冲的Channel
    ch := make(chan string)

    // 启动一个Goroutine,发送数据到Channel
    go func() {
        fmt.Println("Goroutine开始发送数据")
        ch <- "从Goroutine传来的消息"
    }()

    // 模拟延时,表示主Goroutine正在处理其他任务
    time.Sleep(2 * time.Second)

    // 从Channel接收数据
    message := <-ch
    fmt.Println("接收到数据:", message)

    // 创建一个有缓冲的Channel,容量为2
    bufferedCh := make(chan int, 2)

    // 向有缓冲的Channel发送数据,不会立即阻塞
    bufferedCh <- 1
    bufferedCh <- 2

    // 从有缓冲的Channel接收数据
    fmt.Println("从有缓冲Channel接收数据:", <-bufferedCh)
    fmt.Println("从有缓冲Channel接收数据:", <-bufferedCh)
}

  • 无缓冲Channel在没有接收者时,发送操作会阻塞;有缓冲Channel则在缓冲区满时才会阻塞。
  • Channel的正确使用是并发编程中非常重要的部分,需要仔细处理发送和接收的同步问题。

代码示例

package main

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

// 数据处理函数
// 模拟接收一个整数,经过处理后发送到输出Channel
func processData(id int, data int, out chan<- string) {
	// 模拟数据处理耗时
	time.Sleep(time.Duration(rand.Intn(500)) * time.Millisecond)
	result := fmt.Sprintf("Goroutine %d 处理结果:%d", id, data*2) // 假设处理是简单的乘以2
	out <- result
}

func main() {
	// 初始化随机种子
	rand.Seed(time.Now().UnixNano())

	// 创建一个无缓冲的Channel
	dataCh := make(chan string)

	// 使用WaitGroup等待所有Goroutines完成
	var wg sync.WaitGroup

	// 启动多个Goroutines进行数据处理
	const numGoroutines = 5 // Goroutines的数量
	for i := 0; i < numGoroutines; i++ {
		wg.Add(1)
		go func(id int) {
			defer wg.Done()
			data := rand.Intn(100) // 生成一个随机数作为数据
			processData(id, data, dataCh)
		}(i)
	}

	// 启动一个Goroutine用于接收所有处理结果
	go func() {
		wg.Wait()
		close(dataCh)
	}()

	// 从Channel中读取并打印每个Goroutine的处理结果
	for result := range dataCh {
		fmt.Println(result)
	}

	fmt.Println("所有Goroutine处理完成")
}
  1. processData函数:每个Goroutine都会调用这个函数,它接收一个整数数据,处理后(这里简化为乘以2的操作),将结果发送到输出Channel。

  2. 启动多个Goroutines:循环中创建了多个Goroutines,每个都调用processData函数处理数据。

  3. 使用sync.WaitGroupWaitGroup用于等待所有Goroutine完成其工作。每启动一个Goroutine,就调用wg.Add(1),每个Goroutine完成时调用wg.Done()

  4. 关闭Channel:在所有Goroutines完成后,关闭数据Channel。这是通过在另一个Goroutine中调用wg.Wait()close(dataCh)实现的。

  5. 读取Channel数据:主Goroutine循环读取Channel中的数据,并打印结果。

12-27 22:12