Go语言进阶之并发编程 | 青训营笔记

简介: Go语言进阶之并发编程 | 青训营笔记

前言

记录加入青训营的每一天的日笔记

并发编程

并发与并行的区别

并发:多线程程序在一个核的CPU上运行

并行:多线程程序在多个核的CPU上运行

Go可以充分发挥多核优势 高效运行

image.png

协程Goroutine

协程:用户态,轻量级线程 栈MB级别

线程:内核态,线程跑多个协程,栈KB级别

image.png

线程的创建、切换、停止较大地占用系统资源

协程的创建和调度由Go语言进行完成

通过开启协程快速打印hello goroutine案例:

package concurrence
import (
    "fmt"
    "time"
)
func hello(i int) {
    println("hello goroutine : " + fmt.Sprint(i))
}
func HelloGoRoutine() {
    for i := 0; i < 5; i++ {
        // go关键字作为创建协程的关键字
        go func(j int) {
            hello(j)
        }(i)
    }
    // 保证子协程运行完前主线程不退出
    time.Sleep(time.Second)
}

CSP(communicating sequential processes)并发模型

不同于传统的多线程通过共享内存来通信,CSP讲究的是“以通信的方式来共享内存”。

Do not communicate by sharing memory; instead, share memory by communicating. “不要以共享内存的方式来通信,相反,要通过通信来共享内存。”

Channel 缓冲通道

创建方式:

make(chan 元素类型, [缓冲大小])

通道是用来传递数据的一个数据结构,可以用于两个goroutine之间,通过传递一个指定类型的值来同步运行和通讯。

操作符<-用于指定通道的方向,实现发送or接收

若未指定方向,则为双向通道

  • 无缓冲通道 make(chan int)
  • 有缓冲通道 make(chan int, 2)

image.png

通过两个Channel通道完成数字平方任务案例:

package concurrence
func CalSquare() {
    src := make(chan int)
    dest := make(chan int, 3)
    go func() {
        defer close(src)
        for i := 0; i < 10; i++ {
            src <- i
        }
    }()
    go func() {
        defer close(dest)
        for i := range src {
            dest <- i * i
        }
    }()
    for i := range dest {
        //复杂操作
        println(i)
    }
}

注意:

  • 如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
  • 上述代码中之所以能够顺利从通道接收到数据,是因为每次遍历之前都通过关闭对应的通道后再进行的遍历接受数据

并发安全Lock

若采用共享内存实现通信,则会出现多个Goroutine同时操作一块内存资源的情况,这种情况会发生竞态问题(数据竞态)

Mutex互斥锁解决数据竞争

互斥锁是一种常用的控制共享资源访问的方法,它能够保证同时只有一个goroutine可以访问共享资源。Go语言中使用sync包的Mutex类型来实现互斥锁。


package concurrence
import (
    "sync"
    "time"
)
var (
    x    int64
    lock sync.Mutex
)
func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
    }
}
func addWithoutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}
func Add() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithoutLock()
    }
    time.Sleep(time.Second)
    println("WithoutLock:", x)
    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    time.Sleep(time.Second)
    println("WithLock:", x)
}
func ManyGoWait() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(j int) {
            defer wg.Done()
            hello(j)
        }(i)
    }
    wg.Wait()
}

使用互斥锁能够保证同一时间有且只有一个goroutine进入临界区,其他的goroutine则在等待锁;

当互斥锁释放后,等待的goroutine才可以获取锁进入临界区,多个goroutine同时等待一个锁时,唤醒的策略是随机的。

WaitGroup解决数据竞争

Go语言中除了可以使用通道(channel)和互斥锁进行两个并发程序间的同步外,还可以使用等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务 WaitGroup 值在内部维护着一个计数,此计数的初始默认值为零。


package concurrence
import (
    "fmt"
    "sync"
)
func HelloPrint(i int) {
    fmt.Println("Hello WaitGroup :", i)
}
func ManyGoWait() {
    var wg sync.WaitGroup
    wg.Add(5)
    for i := 0; i < 5; i++ {
        go func(j int) {
            defer wg.Done()
            HelloPrint(j)
        }(i)
    }
    wg.Wait()
}
func main() {
    ManyGoWait()
}

小结

今天学习到的内容还需要进一步的消化,我也是打算将并发编程这一块的内容熟悉透彻了再进行下一部分的课程学习。如果笔记中有错误的地方也希望掘友们可以及时的提出纠正。


目录
相关文章
|
5天前
|
存储 JSON 监控
Viper,一个Go语言配置管理神器!
Viper 是一个功能强大的 Go 语言配置管理库,支持从多种来源读取配置,包括文件、环境变量、远程配置中心等。本文详细介绍了 Viper 的核心特性和使用方法,包括从本地 YAML 文件和 Consul 远程配置中心读取配置的示例。Viper 的多来源配置、动态配置和轻松集成特性使其成为管理复杂应用配置的理想选择。
23 2
|
9天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
|
3天前
|
Go 索引
go语言中的循环语句
【11月更文挑战第4天】
11 2
|
3天前
|
Go C++
go语言中的条件语句
【11月更文挑战第4天】
14 2
|
8天前
|
Ubuntu 编译器 Linux
go语言中SQLite3驱动安装
【11月更文挑战第2天】
30 7
|
8天前
|
关系型数据库 Go 网络安全
go语言中PostgreSQL驱动安装
【11月更文挑战第2天】
38 5
|
8天前
|
安全 Go
用 Zap 轻松搞定 Go 语言中的结构化日志
在现代应用程序开发中,日志记录至关重要。Go 语言中有许多日志库,而 Zap 因其高性能和灵活性脱颖而出。本文详细介绍如何在 Go 项目中使用 Zap 进行结构化日志记录,并展示如何定制日志输出,满足生产环境需求。通过基础示例、SugaredLogger 的便捷使用以及自定义日志配置,帮助你在实际开发中高效管理日志。
24 1
|
7天前
|
程序员 Go
go语言中的控制结构
【11月更文挑战第3天】
83 58
|
6天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
7天前
|
存储 编译器 Go
go语言中的变量、常量、数据类型
【11月更文挑战第3天】
24 9