美文网首页
通过三个例子,学习 Go 语言并发编程的利器 - gorouti

通过三个例子,学习 Go 语言并发编程的利器 - gorouti

作者: 华山令狐冲 | 来源:发表于2024-02-20 10:12 被阅读0次

Go 语言(也称为 Golang)是一门由 Google 开发的编程语言,以其简洁、高效和对并发编程的内置支持而在编程领域享有盛名。

在 Go 语言中,goroutine 是一项强大的并发特性,用于轻量级线程的创建和管理。

本文将向没有接触过 Go 语言的朋友,介绍 goroutine 的概念、使用场合,并提供具体的例子以演示其用法。

1. Goroutine 的概念:

Goroutine 是 Go 语言中用于并发执行的轻量级线程。与传统的线程相比,goroutine 的创建和销毁成本很低,可以在同一个程序中轻松创建多个 goroutine 而不会导致性能下降。Goroutines 之间通过通道(channel)进行通信,实现了并发编程的简洁和安全。

2. Goroutine的使用场合:

俗话说,红粉赠佳人,宝剑送烈士。既然 Goroutine 有如此突出的能力,并发编程领域就是它大显身手的舞台。

在下列这些应用场景里,我们都能看到 Goroutine 施展拳脚的身影。

  • 并发执行任务: Goroutine 可用于同时执行多个任务,提高程序的性能。
  • 非阻塞 I/O 操作: 在进行 I/O 操作时,可以使用 goroutine 确保其他任务继续执行,而不是同步等待 I/O 完成。
  • 事件驱动编程: Goroutine 可用于处理事件,如监听 HTTP 请求、处理用户输入等。
  • 并发算法: 实现一些需要并行计算的算法,通过 goroutine 可以更轻松地管理并发执行的部分。
  • 定时任务: 使用 goroutine 和定时器可以实现定时执行的任务。

3. Goroutine 的具体例子:

我们通过一些具体的例子来说明。

例子1:简单的并发任务执行

作为热身,我们通过一些简单的 Hello World 级别的例子来熟悉 Goroutine 的用法。

源代码如下:

package main

import (
    "fmt"
    "sync"
)

func printNumbers(wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 1; i <= 5; i++ {
        fmt.Printf("%d ", i)
    }
}

func printLetters(wg *sync.WaitGroup) {
    defer wg.Done()
    for char := 'a'; char <= 'e'; char++ {
        fmt.Printf("%c ", char)
    }
}

func main() {
    var wg sync.WaitGroup

    wg.Add(2)
    go printNumbers(&wg)
    go printLetters(&wg)

    wg.Wait()
}

将上述源代码另存为一个 .go 文件里,比如取名 1.go, 然后执行命令行 go run 1.go, 看到下面的输出:

在这个例子中,我们通过关键字 go,创建了两个 goroutine,一个用于打印数字,另一个用于打印字母。sync.WaitGroup 函数调用,用于等待这两个 goroutine 执行完毕。这是 go 语言并发编程里,最常用的同步机制之一。

例子2:并发下载多个网页

源代码如下:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "sync"
)

func fetch(url string, wg *sync.WaitGroup) {
    defer wg.Done()

    response, err := http.Get(url)
    if err != nil {
        fmt.Printf("Error fetching %s: %v\n", url, err)
        return
    }
    defer response.Body.Close()

    body, err := ioutil.ReadAll(response.Body)
    if err != nil {
        fmt.Printf("Error reading response body from %s: %v\n", url, err)
        return
    }

    fmt.Printf("Length of %s: %d\n", url, len(body))
}

func main() {
    var wg sync.WaitGroup

    urls := []string{"https://www.baidu.com", "https://cloud.tencent.com/", "https://www.qq.com/"}

    for _, url := range urls {
        wg.Add(1)
        go fetch(url, &wg)
    }
    wg.Wait()
}

执行上面的源代码后,在控制台看到下列输出,打印了三个网站(百度,qq 和腾讯云社区)首页 html 文件的长度(length)。


这个例子展示了如何使用 goroutine 并发地下载多个网页。每个网页都在单独的goroutine中进行下载,从而减少了完成下载任务所需花费的时间。

从源代码可以看出,如果是用 Java 这门传统的编程语言完成这个需求的编码工作,我们需要使用 Java 提供的 Thread 类和一些繁琐的同步机制代码。而使用 Go 语言,通过 go fetch(url, &wg) 这一行短小精悍的语言,就可以轻松完成 goroutine 的启动和根据 url 读取 HTML 页面数据的操作,代码大大简化。

例子3:通过通道实现生产者-消费者模型

生产者-消费者是操作系统课程里必学的概念之一。我们使用 func 关键字,分别定义了生产者和消费者两个函数。生产者函数,使用 time.Sleep 方法,每隔 0.5 秒钟生产一个正整数序列 1,2,3,4,5. 消费者函数,相应的每隔 0.5 秒钟消费一个正整数。

package main

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

func producer(ch chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 1; i <= 5; i++ {
        ch <- i
fmt.Printf("Produced: %d\n", i)
        time.Sleep(time.Millisecond * 500) // 模拟生产过程的延迟
    }
    close(ch)
}

func consumer(ch <-chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for num := range ch {
        fmt.Printf("Consumed: %d\n", num)
        time.Sleep(time.Millisecond * 200) // 模拟消费过程的延迟
    }
}

func main() {
    var wg sync.WaitGroup

    ch := make(chan int, 3) // 创建带缓冲通道,容量为3

    wg.Add(2)
    go producer(ch, &wg)
    go consumer(ch, &wg)

    wg.Wait()
}

执行上面的 go 源代码,输出如下,生产一个正整数序列,然后消费,以此类推。


这个例子演示了如何使用 goroutine 和通道实现生产者-消费者模型。生产者将数据发送到通道,而消费者从通道中接收并处理数据,通过 goroutine 实现了并发的生产和消费过程。

我们使用了带缓冲的通道,容量为3。带缓冲的通道允许在通道未被完全读取的情况下进行写入,这在生产者和消费者速度不一致时非常有用。

在生产者函数 producer 里,在ch 是一个只写的整数通道,wg 是一个 sync.WaitGroup 类型的指针,用于等待所有的goroutine完成。在这个函数中,生产者通过 ch <- i 向通道发送整数 i,表示生产了一个产品。然后,通过 fmt.Printf 打印出生产的产品的信息,并通过 time.Sleep 模拟生产过程的延迟。最后,通过 close(ch) 关闭通道,表示生产者不再向通道发送数据。

再看消费者函数 consumer. ch 是一个只读的整数通道,wg 是一个 sync.WaitGroup 类型的指针。在这个函数中,消费者通过 num := <-ch 从通道接收整数,表示消费了一个产品。然后,通过 fmt.Printf 打印出消费的产品的信息,并通过 time.Sleep 模拟消费过程的延迟。这个循环会一直执行,直到通道被关闭,此时 range ch 将会退出。

总结

通过本文介绍的这三个例子,我们可以看到 goroutine 的强大之处。通过goroutine 和 channel 的组合,以及 sync.WaitGroup 的使用,Go 语言提供了强大而简洁的并发编程机制。相信之前有过 Java 编程经验的开发者,对于 Java 和 Go 这两门编程语言,在实现同一需求所需的不同代码量的差异,更是深有体会。

相关文章

  • Go基础语法(九)

    Go语言并发 Go 是并发式语言,而不是并行式语言。 并发是指立即处理多个任务的能力。 Go 编程语言原生支持并发...

  • Go并发

    Go语言中的并发编程 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很...

  • Go语言基础6 - 并发

    概述 我们将用几节来学习Go语言基础,本文结构如下: 1. 并发 1.1 通过通信共享内存 在并发编程中,为实现对...

  • go并发通信

    go并发编程时,请记住:“不要通过共享内存来通信,而应该通过通信来共享内存” channel是Go语言在语言级别提...

  • 一文读懂 Go sync.Cond 设计

    Go 语言通过 go 关键字开启 goroutine 让开发者可以轻松地实现并发编程,而并发程序的有效运行,往往离...

  • Go语言简介

    Go语言简介 Go语言设计的初衷 针对其他语言的痛点进行设计并加入并发编程为大数据,微服务,并发而生的通用编程语言...

  • go语言开发培训班哪里好

    Go作为专门为并发和大数据设计的语言,在编程界占据越来越重要的地位!越来越多的人开始学习go编程语言,go语言开发...

  • 第十篇:并发编程

    Go语言区别于其它语言最核心的优势就是:Go对并发编程的大力支持。 一来Go是通过协程goroutine作为执行体...

  • GO学习笔记(18) - 并发编程(1) - 理解gorouti

    目录 进程、线程与协程 并发模模型介绍 GO并发编程介绍 进程、线程与协程 进程和线程 进程是程序在操作系统中的一...

  • Go的栈空间管理

    栈空间管理的基本逻辑 go语言通过goroutine提供了并发编程支持,goroutine是go运行库的功能,而不...

网友评论

      本文标题:通过三个例子,学习 Go 语言并发编程的利器 - gorouti

      本文链接:https://www.haomeiwen.com/subject/rtugndtx.html