day3:语言进阶1 | 青训营

133 阅读3分钟

1.Goroutine

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

使用语法:

go 函数名( 参数列表 )
//例如
go add(a,b)

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

课程示例:

package main
import (
	"time"
	"fmt"
)
func hello(i int){
	println("hello goroutine:" + fmt.Sprint(i))
}
func HelloGoRoutine(){
	for i := 0; i < 5; i++ {
		go func(j int){
			hello(j)
		}(i)  //传递变量 `i` 到参数 `j` 中,
	}
	time.Sleep(time.Second)
}
func main() {
	HelloGoRoutine()
}

解析代码:

  1. 在上述代码中,我们定义了一个名为 hello 的函数,用于打印 "hello goroutine:" 和一个整数。
  2. HelloGoRoutine函数中,我们使用一个 for 循环启动了5个Goroutine。每个Goroutine执行一个匿名函数,接收变量 i 作为参数 j 。
  3. 在匿名函数中,我们调用hello函数并传递参数 j 来打印对应的信息。
  4. 为了确保主线程能够等待所有 Goroutine 完成后再退出,我们使用了time.Sleep(time.Second) 来让主线程休眠 1 秒。
  5. 通过使用 Goroutine 并发执行,我们可以同时执行多个任务,提高程序的处理效率。
  6. 为了确保主线程能够正确等待所有 Goroutine 完成,我们需要使用适当的同步方法来确保正确处理所有的并发操作。在这个例子中,使用 time.Sleep 来简单等待所有 Goroutine 完成。

执行后得到

hello goroutine:2

hello goroutine:4

hello goroutine:3

hello goroutine:1

hello goroutine:0

可以看出,hello goroutine: 是没有固定先后顺序。因为它们是五个 goroutine 并发执行。

扩展:

确保主线程能够正确等待所有 Goroutine 完成,可以使用 sync.WaitGroup 等来同步等待所有 Goroutine 完成。

package main
import (
	"sync"
	"fmt"
)
var wg sync.WaitGroup
func hello(i int) {
	defer wg.Done() // goroutine结束就登记-1
	fmt.Println("Hello Goroutine!", i)
}
func main() {
	for i := 0; i < 5; i++ {
		wg.Add(1) // 启动一个goroutine就登记+1
		go hello(i)
}
	wg.Wait() // 等待所有登记的goroutine都结束
}

执行后得到

Hello Goroutine! 1

Hello Goroutine! 2

Hello Goroutine! 0

Hello Goroutine! 3

Hello Goroutine! 4

5个goroutine并发执行,而调度goroutine是随机的,因此输出顺序不一致

其他参考资料:segmentfault.com/a/119000001…

2.CSP(Communicating Sequential Processes)

概念:

CSP(Communicating Sequential Processes)是一种并发编程模型。 Go语言是一门支持CSP模型的编程语言,提供了简洁而强大的并发原语来支持并发编程。 在Go语言中,CSP模型的核心概念是通过通信来共享数据,而不是通过共享数据来通信

3.channel(通道)

概念:

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

发送、接收数据:

操作符 <- 用于指定通道的方向,发送或接收。 如果未指定方向,则为双向通道。

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

声明通道:chan关键字

创建通道:make关键字

无缓冲区:ch := make(chan int)发送端发送数据,同时必须有接收端相应的接收数据。

有缓冲区:ch := make(chan int, 100) 第二个参数(100)指定缓冲区大小

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

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

注意

如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。

如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;

如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。

Go 遍历通道与关闭通道

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

Go并发参考资料:www.runoob.com/go/go-concu…