开心档之 Go 并发

简介: 开心档之 Go 并发


Go 并发

Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。

goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。

goroutine 语法格式:

go 函数名( 参数列表 )


例如:

go f(x, y, z)


开启一个新的 goroutine:

f(x, y, z)


Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。

实例

package main

import ("fmt""time")

func say(s string) {for i := 0; i < 5; i++ {time.Sleep(100 * time.Millisecond)fmt.Println(s)}}

func main() {go say("world")say("hello")}

执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:

world
hello
hello
world
world
hello
hello
world
world
hello




通道(channel)

通道(channel)是用来传递数据的一个数据结构。

通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v // 把 v 发送到通道 ch
v := <-ch // 从 ch 接收数据
// 并把值赋给 v


声明一个通道很简单,我们使用 chan 关键字即可,通道在使用前必须先创建:

ch := make(chan int)


注意:默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。

以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:

实例

package main

import "fmt"

func sum(s []int, c chan int) {sum := 0for _, v := range s {sum += v}c <- sum // 把 sum 发送到通道 c}

func main() {s := []int{7, 2, 8, -9, 4, 0}

c := make(chan int)go sum(s[:len(s)/2], c)go sum(s[len(s)/2:], c)x, y := <-c, <-c // 从通道 c 中接收

fmt.Println(x, y, x+y)}

输出结果为:

-5 17 12


通道缓冲区

通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:

ch := make(chan int, 100)


带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。

不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。

注意:如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

实例

package main

import "fmt"

func main() {// 这里我们定义了一个可以存储整数类型的带缓冲通道// 缓冲区大小为 2ch := make(chan int, 2)

// 因为 ch 是带缓冲的通道,我们可以同时发送两个数据// 而不用立刻需要去同步读取数据 ch <- 1ch <- 2

// 获取这两个数据 fmt.Println(<-ch)fmt.Println(<-ch)}

执行输出结果为:

1
2


Go 遍历通道与关闭通道

Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:

v, ok := <-ch


如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。

实例

package main

import ("fmt")

func fibonacci(n int, c chan int) {x, y := 0, 1for i := 0; i < n; i++ {c <- xx, y = y, x+y}close(c)}

func main() {c := make(chan int, 10)go fibonacci(cap(c), c)// range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个// 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据// 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不// 会结束,从而在接收第 11 个数据的时候就阻塞了。for i := range c {fmt.Println(i)}}

执行输出结果为:

0
1
1
2
3
5
8
13
21
34



相关文章
|
1月前
|
Go 调度 开发者
CSP模型与Goroutine调度的协同作用:构建高效并发的Go语言世界
【2月更文挑战第17天】在Go语言的并发编程中,CSP模型与Goroutine调度机制相互协同,共同构建了高效并发的运行环境。CSP模型通过通道(channel)实现了进程间的通信与同步,而Goroutine调度机制则确保了并发任务的合理调度与执行。本文将深入探讨CSP模型与Goroutine调度的协同作用,分析它们如何共同促进Go语言并发性能的提升。
|
2月前
|
存储 安全 Java
浅谈Go并发原语
浅谈Go并发原语
35 0
|
1月前
|
程序员 Go 数据处理
|
1月前
|
SQL Go 数据库
【Sentinel Go】新手指南、流量控制、熔断降级和并发隔离控制
【2月更文挑战第12天】随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 是面向分布式、多语言异构化服务架构的流量治理组件,主要以流量为切入点,从流量路由、流量控制、流量整形、熔断降级、系统自适应过载保护、热点流量防护等多个维度来帮助开发者保障微服务的稳定性。
57 0
|
1月前
|
监控 Java 编译器
Go语言内存与并发性能综合优化策略
【2月更文挑战第11天】Go语言以其高效的并发处理能力和简洁的内存管理机制成为了现代软件开发中的热门选择。然而,在实际应用中,如何综合优化Go程序的内存使用和并发性能,仍然是一个值得探讨的话题。本文将深入探讨Go语言内存与并发性能的综合优化策略,包括内存布局优化、并发模式设计、资源池化以及性能监控与分析等方面,旨在帮助开发者全面提升Go程序的整体性能。
|
1月前
|
Java Go 调度
Go语言并发性能提升实战
【2月更文挑战第11天】Go语言以其出色的并发处理能力在高性能应用中占据了一席之地。然而,要想充分利用Go的并发优势,实现性能的最大化,还需要掌握一些关键的优化技巧。本文将深入探讨Go语言并发性能提升的关键技术,包括goroutine调度、channel使用、sync原语选择等方面,帮助开发者更好地理解和优化Go程序的并发性能。
|
2月前
|
消息中间件 Go
Go语言高级并发编程技术:深入探索同步原语与复杂并发模式
【2月更文挑战第6天】在Go语言中,除了基础的goroutine和channel之外,还提供了丰富的同步原语和高级并发模式。本文将深入探讨Go语言中的sync包、atomic包以及更复杂的并发模式,如管道、消息队列和生产者消费者模型。通过理解这些高级并发编程技术,我们可以更好地构建高性能、可扩展的并发系统。
|
2月前
|
Go 调度 开发者
Go语言并发基础:轻量级线程与通道通信
【2月更文挑战第6天】本文介绍了Go语言在并发编程方面的基础知识和核心概念。我们将深入探讨goroutine(轻量级线程)的创建与调度,以及如何利用channel进行goroutine间的通信与同步。此外,还将简要提及select语句的使用,并解释其在处理多个channel操作时的优势。
|
2月前
|
存储 安全 Java
深入浅出Go并发之协程—goroutine
深入浅出Go并发之协程—goroutine
35 0
|
3月前
|
Go
通道多路复用:Go语言并发编程的黄金法则
通道多路复用:Go语言并发编程的黄金法则
33 0

热门文章

最新文章