Go 语言 Goroutine - 轻量级线程

简介: Go 语言 Goroutine - 轻量级线程

概述

在 Go 语言里,Goroutine 是一种轻量级的线程实现。

它的出现使得并发编程变得更加容易,无需担心底层线程的复杂管理,Goroutine 会被 Go 的运行时(runtime)智能地调度。

本文将介绍 Goroutine,从基础概念到实际应用,将探索并发编程的魔力。

主要内容包括

Goroutine 的定义与特点

Goroutine 的创建与启动

Goroutine 的同步与等待

Goroutine 的调度与性能制

Goroutine 的错误处理

Goroutine 的最佳实践


 

1. Goroutine 的定义与特点

Goroutine 是 Go 语言中的轻量级线程实现,它由 Go 的运行时系统管理。

与传统线程相比,Goroutine 更加轻量、高效,数以千计的 Goroutine 可以在一个应用程序中同时运行,而不会引起明显的性能开销。


 

2. Goroutine 的创建与启动

2.1

package main
import (  "fmt"  "time")
func main() {  go printNumbers()  go printLetters()
  // 等待一段时间,确保goroutines有足够时间执行  time.Sleep(1 * time.Second)}
func printNumbers() {  for i := 1; i <= 5; i++ {    fmt.Println("Number:", i)    time.Sleep(100 * time.Millisecond)  }}
func printLetters() {  for i := 'a'; i <= 'e'; i++ {    fmt.Println("Letter:", string(i))    time.Sleep(150 * time.Millisecond)  }}

在上面示例中,使用 go 关键字创建了两个 Goroutine,分别用于打印数字和字母。

用 go 关键字,函数会在一个新的 Goroutine 中异步执行,不会阻塞主程序的运行。


package main
import (  "fmt"  "sync")
func main() {  var wg sync.WaitGroup  wg.Add(2)
  go func() {    defer wg.Done()    fmt.Println("Goroutine 1")  }()
  go func() {    defer wg.Done()    fmt.Println("Goroutine 2")  }()
  wg.Wait()}

在上面示例中,使用了 sync.WaitGroup 来等待两个 Goroutine 的执行完成。

这种方式可以确保所有 Goroutine 都执行完毕后再继续主程序的执行。


 

3. Goroutine 的同步与等待

3

package main
import (  "fmt"  "sync")
func main() {  var wg sync.WaitGroup  wg.Add(2)
  go func() {    defer wg.Done()    fmt.Println("Goroutine 1")  }()
  go func() {    defer wg.Done()    fmt.Println("Goroutine 2")  }()
  wg.Wait()  fmt.Println("All Goroutines have finished.")}

在这个例子中,sync.WaitGroup 用来等待两个 Goroutine 执行完成。

Add(2) 表示需要等待两个 Goroutine 完成,每个 Goroutine 执行完毕时调用 Done() 来通知 WaitGroup。

最后,Wait() 会阻塞主程序,直到所有 Goroutine 都执行完毕。

3.2

package main
import (  "fmt")
func main() {  ch := make(chan string)    go sendData(ch)  msg := <-ch  fmt.Println("Received message:", msg)}
func sendData(ch chan string) {  ch <- "Hello, Goroutine!"}

在这个例子中,创建了一个字符串类型的 Channel,并将其传递给 sendData 函数。

sendData 函数将字符串消息发送到 Channel,主程序通过 <-ch 接收 Channel 中的消息。

这种方式可以实现 Goroutine 之间的简单通信。


 

4. Goroutine 的调度与性能

4.1 Goroutine 的调度器

Go 语言的运行时系统包含了一个 Goroutine 调度器,它负责在多个操作系统线程上调度大量的 Goroutine。

这种调度方式可以使得 Goroutine 在多核处理器上充分利用并发。

4.2 Goroutine 的性能优化

在并发编程中,资源的浪费是一个常见的问题。

合理地控制 Goroutine 的数量,避免创建过多的 Goroutine,可以避免资源的浪费。


 

5. Goroutine 的错误处理

5.1

package main
import (  "errors"  "fmt")
func main() {  ch := make(chan int)
  go func() {    defer func() {      if r := recover(); r != nil {        fmt.Println("Recovered from panic:", r)      }    }()
    if err := performTask(); err != nil {      panic(err)    }
    ch <- 1  }()
  <-ch}
func performTask() error {  return errors.New("went wrong")}

在上面示例中,在 Goroutine 中执行了 performTask() 函数,如果函数返回错误,用 panic() 触发了一个异常。

在 Goroutine 中使用 recover() 可以捕获异常,使得程序不会因为一个 Goroutine 的错误而崩溃。

5.2

package main
import (  "fmt"  "sync"  "time")
func main() {  var wg sync.WaitGroup  done := make(chan struct{})
  for i := 0; i < 3; i++ {    wg.Add(1)    go func(id int) {      defer wg.Done()
      for {        select {        case <-done:          fmt.Printf("Goroutine %d received done signal.\n", id)          return        default:          fmt.Printf("Goroutine %d is working...\n", id)          time.Sleep(500 * time.Millisecond)        }      }    }(i)  }
  time.Sleep(2 * time.Second)  close(done) // 发送关闭信号  wg.Wait()}

在上面例子中,启动了 3 个 Goroutine,它们会一直工作直到接收到 done 通道的关闭信号。

通过 close(done),向所有的 Goroutine 发送了关闭信号,使得它们能够安全、优雅地退出。


 

6. Goroutine 的最佳实践

避免共享内存:Goroutine 之间的通信最好通过 Channel 进行,避免共享内存,从而减少数据竞争的可能性。

合理控制 Goroutine 数量:不要随意创建大量的 Goroutine,合理地控制 Goroutine 的数量可以避免资源浪费。

优雅处理 Goroutine 错误:使用 recover() 捕获 Goroutine 中的异常,确保程序不会因为一个 Goroutine 的错误而崩溃。

确保 Goroutine 退出:Goroutine 应该有退出的机制,确保在不需要时能够安全地退出,避免资源泄露。


 

7. 总结

Goroutine 是 Go 语言并发编程的核心特性,它简化了并发编程的复杂性,提供了一种高效、简洁的并发解决方案。

通过合理的创建、同步和调度,可以编写出高效、稳定的并发程序。

目录
相关文章
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
571 2
|
Cloud Native 安全 Java
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
852 1
|
Cloud Native Go API
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
740 0
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
575 0
|
Cloud Native Java 中间件
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
608 0
|
Cloud Native Java Go
Go:为云原生而生的高效语言
Go:为云原生而生的高效语言
1757 0
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
数据采集 消息中间件 编解码
Go语言实战案例:使用 Goroutine 并发打印
本文通过简单案例讲解 Go 语言核心并发模型 Goroutine,涵盖协程启动、输出控制、主程序退出机制,并结合 sync.WaitGroup 实现并发任务同步,帮助理解 Go 并发设计思想与实际应用。
|
数据采集 JSON Go
Go语言实战案例:实现HTTP客户端请求并解析响应
本文是 Go 网络与并发实战系列的第 2 篇,详细介绍如何使用 Go 构建 HTTP 客户端,涵盖请求发送、响应解析、错误处理、Header 与 Body 提取等流程,并通过实战代码演示如何并发请求多个 URL,适合希望掌握 Go 网络编程基础的开发者。
|
JSON 前端开发 Go
Go语言实战:创建一个简单的 HTTP 服务器
本篇是《Go语言101实战》系列之一,讲解如何使用Go构建基础HTTP服务器。涵盖Go语言并发优势、HTTP服务搭建、路由处理、日志记录及测试方法,助你掌握高性能Web服务开发核心技能。

热门文章

最新文章