Go 并发实现

85 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第6天,点击查看活动详情

Go 并发

  • 并发: 逻辑上同时处理多个任务的能力
  • 并行:物理行通一个时刻执行多个并发任务

多线程和多进程是并行的基本条件,但是单线程可以用到协程(coroutine)做到并发,尽管协程在单线程上是通过主动切换来实现多任务并发。

  • 用多进程来实现分布式和负载均衡,减轻单进程垃圾回收带来的压力
  • 用多线程来抢夺更多的处理器资源,
  • 用协程来提高处理器的时间片利用率

简单的 goroutine 归纳为协程并不合适, 运行时会创建多个线程来执行任务,任务单元调度到其他线程并行执行

只需要在函数前 添加 go 关键字即可创建并发任务

package main

import (
	"fmt"
	"time"
)

var c int

func counter() int {
	c++
	return c
}

func main() {
	a := 100
	go func(x, y int) {
		time.Sleep(time.Second)
		fmt.Println("go:", x, y)
	}(a, counter())


	a += 100

	fmt.Println("main:", a, counter())

	time.Sleep(time.Second *3)
}

运行结果


main: 200 2
go: 100 1

Process finished with exit code 0

Wait

进程退出时,不会等待并发任务结束,可用通道(channel)阻塞,然后发出退出信号。

package main

import (
	"fmt"
)

func Add(a, b int){
	z := a + b
	fmt.Println("z:",z)
}

func main() {
	for i := 0; i < 10; i++ {
		go Add(i,i)
	}

	//time.Sleep(time.Second * 1000)  // 没有这一行会发现什么都没有,因为主线程不等待其他 goroutine 结束
}

工程上常见的并发通信模型: 共享数据和消息

不要通过共享内存来通信,要通过通信来共享内存

channel

channel 是 Go 语言提供的 goroutine 间的通信方式, 我们可以使用 channel 在两个或者多个 goroutine 之间进行消息传递, channel 是进程内的通信方式, 因此,通过 channel 对象传递的过程和调用函数时传递的参数行为比较一致,比如也可以传递指针。

channel 是类型相关的,也就是说, 一个 channel 只能传递一种类型的值, 这个类型需要在声明 channel 时指定,

package main

import "fmt"

func Add(a, b int, ch chan int) {
	fmt.Println("Counting")
	ch <- a+b
}

func main() {
	chs := make([]chan int, 10)
	for i :=0; i < 10; i++ {
		chs[i] = make(chan int)
		go Add(i, i, chs[i])
	}

	for _, ch := range  chs {
		val := <- ch
		fmt.Println(val)
	}
}

执行结果
Counting
Counting
Counting
Counting
Counting
Counting
Counting
Counting
Counting
0
2
Counting
4
6
8
10
12
14
16
18