第八章 并发编程——1.并发编程介绍

一、引言

并发编程是现代软件开发中的重要技术,它允许程序同时处理多个任务,提高程序的执行效率和响应速度。Go语言作为一种高效、简洁的编程语言,天生支持并发编程,提供了goroutine和channel等强大的并发原语。本笔记将详细介绍Go语言并发编程的基本概念、常用技术以及示例代码,帮助读者更好地理解和掌握Go语言并发编程。

二、基本概念
  1. 串行(Serial)
    • 串行执行是指按照顺序一个接一个地执行任务或操作。
    • 在串行执行中,每个任务必须在前一个任务完成后才能开始执行。
  1. 并发(Concurrency)
    • 并发是指同时处理多个任务或操作,但在时间上可以重叠,不一定是同时执行。
    • 操作系统或编程语言通过任务切换或调度机制来实现并发,使得多个任务可以交替执行,并共享系统资源。
  1. 并行(Parallel)
    • 并行是指同时执行多个任务或操作,每个任务都在不同的处理器核心或计算单元上独立执行。
    • 并行执行要求系统具有多个可同时执行任务的资源,如多核处理器或分布式计算集群。
  1. 进程(Process)
    • 进程是操作系统中的一个执行实体,具有独立的内存空间、程序计数器、寄存器、文件句柄等资源。
    • 一个进程可以包含多个线程,它们共享进程的资源。
  1. 线程(Thread)
    • 线程是进程中的执行单元,是操作系统调度执行的最小单位。
    • 一个进程可以包含多个线程,它们共享进程的地址空间和资源。
    • 线程之间切换的开销较小,可以实现更高效的并发执行。
  1. 协程(Coroutine)
    • 协程是一种轻量级的线程,也被称为用户态线程或纤程。
    • 协程由程序自身控制,而不是由操作系统调度。
    • 协程可以在同一个线程内切换执行,避免了线程切换的开销。
    • 协程通常用于处理高并发、I/O密集型和事件驱动的任务。
三、Go语言并发编程

Go语言通过goroutine和channel提供了强大的并发编程模型。

  1. goroutine
    • goroutine是Go语言中的一种轻量级线程,由Go运行时管理。
    • 创建一个goroutine非常简单,只需在函数调用前加上go关键字。
    • 例如:
package main
 
import (
    "fmt"
    "time"
)
 
func sayHello() {
    fmt.Println("Hello, World!")
}
 
func main() {
    go sayHello() // 创建一个goroutine执行sayHello函数
    time.Sleep(time.Second) // 等待一秒,确保goroutine有时间执行
}
    • 注意:在上面的示例中,sayHello函数在goroutine中执行,主函数不会等待它完成。因此,我们使用了time.Sleep来确保主函数在退出前有足够的时间让goroutine执行。然而,在实际应用中,更好的做法是使用channel或其他同步机制来协调goroutines之间的执行。
  1. channel
    • channel是Go语言中用于在不同goroutine之间进行通信的一种类型安全的管道。
    • 你可以使用channel在goroutines之间传递数据,以实现同步和协作。
    • 例如:
package main
 
import (
    "fmt"
)
 
func sum(a []int, c chan int) {
    total := 0
    for _, v := range a {
        total += v
    }
    c <- total // 将计算结果发送到channel
}
 
func main() {
    a := []int{1, 2, 3, 4, 5}
    c := make(chan int)
 
    go sum(a[:len(a)/2], c) // 第一个goroutine计算前半部分和
    go sum(a[len(a)/2:], c) // 第二个goroutine计算后半部分和
 
    x, y := <-c, <-c // 从channel接收数据
 
    fmt.Println(x, y, x+y) // 输出两部分和以及总和
}
    • 在这个示例中,我们创建了两个goroutine来计算数组a的前半部分和后半部分和,并通过channel将结果传递给主函数。主函数从channel中接收这两个结果,并计算它们的总和。
四、示例代码

以下是一个综合示例,展示了如何使用goroutine和channel来实现一个简单的并发计算任务:

package main
 
import (
    "fmt"
    "math/rand"
    "sync"
    "time"
)
 
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done() // 标记该goroutine已完成工作
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        time.Sleep(time.Second * time.Duration(rand.Intn(2))) // 模拟工作负载
        fmt.Printf("Worker %d finished job %d\n", id, j)
        results <- j * 2 // 将处理结果发送到results channel
    }
}
 
func main() {
    const numJobs = 5
    const numWorkers = 3
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
 
    var wg sync.WaitGroup
 
    // 启动工作goroutines
    for w := 1; w <= numWorkers; w++ {
        wg.Add(1)
        go worker(w, jobs, results, &wg)
    }
 
    // 发送工作到jobs channel
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs) // 关闭jobs channel,表示没有更多的工作要发送
 
    // 等待所有工作goroutines完成
    go func() {
        wg.Wait()
        close(results) // 所有工作完成后关闭results channel
    }()
 
    // 接收并打印结果
    for result := range results {
        fmt.Println("Result:", result)
    }
}

在这个示例中,我们创建了三个worker goroutines来处理五个工作项。每个worker从jobs channel中接收一个工作项,处理它(在这里是将工作项乘以2),并将结果发送到results channel。我们使用sync.WaitGroup来等待所有worker goroutines完成工作,并在所有工作完成后关闭results channel。最后,我们从results channel中接收并打印所有结果。

五、总结

Go语言的并发编程模型提供了高效、简洁的方式来处理并发任务。通过goroutine和channel,我们可以轻松地实现任务的并发执行和通信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lee_yanyi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值