龙空技术网

对比着学 Go 语言-进阶:从基础开讲什么是并发?

实战君 56

前言:

此刻姐妹们对“多进程并发的局限性是什么”大体比较关切,小伙伴们都想要剖析一些“多进程并发的局限性是什么”的相关知识。那么小编也在网上收集了一些对于“多进程并发的局限性是什么””的相关知识,希望兄弟们能喜欢,大家快快来学习一下吧!

并发意味着程序在运行时有多个执行上下文,对应着多个调用栈。

每一个进程在运行时,都有自己的调用栈和堆,有一个完整的上下文。

从系统的角度讲,多个进程时可以并发的。

并发适用的场景有 4 种:

同时响应图形用户界面和 IO 密集操作服务器面对大量用户请求发挥计算机硬件的能力IO 操作阻塞程序

并发的优势有 3 条:

表现问题模型提高程序的执行效率充分利用固件的异步性

实现并发的主流模型包括 4 种:

多进程,传统并发模型多线程,传统并发模型基于回调的非阻塞/异步 IO,传统并发模型协程

传统并发模型的缺陷包括 2 条:

共享内存的并发,容易在工程中遇到各种奇怪的故障和问题消息传递系统,复制操作在性能上不优越。新的并发方式:协程

Python 中的并发方式叫 协程,用 yield 实现,Go 中把它叫做 goroutine , 也称作协程。Go 中从语言的角度就支持协程。

goroutine 由 Go 的运行时 runtime管理。

使用并发执行一个 Add 函数

func Add(x, y int) {  z := x + y  fmt.Println(z)}go Add(1, 1)

在函数调用前加上 go 关键字,这次调用就会在一个新的 goroutine 中并发执行。

当被调用的函数返回时,这个 goroutine 也自动结束了。

当这个函数有返回值,在并发执行时,这个返回值会被丢弃。

package mainimport "fmt"func Add(x, y int) {  z := x + y  fmt.Println(z)}func main() {  for i := 0; i<10; i++ {   		go Add(i,i)   }}

想要并行执行 10 次 Add() 函数,可以通过在一个 for 循环中调用 10 次 Add()。但这样会出现协程还未开始执行,主程序就已经退出的情况。

Go 语言的程序机制是,程序从初始化 main package 并执行 main() 函数开始,当 main() 函数返回时,程序退出,且程序不等待其他 goroutine 结束。

解决的办法是使用 Go 的 channel:

package mainimport "fmt"import "sync"import "runtime"var counter int = 0func Count(lock *sync.Mutex) {     lock.Lock()     counter++     fmt.Println(counter)     lock.Unlock()}func main() {  lock := &sync.Mutex{}    for i:=0; i<10; i++ {      go Count(lock)   }    for {     lock.Lock()          c := counter          lock.Unlock()         runtime.Gosched()     if c>=10 {        break      }  }}

Go 中获知所有 goroutine 的状态,使用的是并发通信。

区别于共享数据的并发通信模型,Go 采用的是消息的模型。

package main import "fmt"func Count(ch chan int) {    fmt.Println("Counting") // 3.执行加减计算    ch <- 1 // 4.通过 ch <- 1 语句向对应的 channel 中写入数据 1,在这个 channel 被读取前,这个操作是阻塞的。}func main() {     chs := make([]chan int, 10) // 1.定义一个包含 10 个 channel 的数组     for i := 0; i<10; i++ { // 2.把数组中的每个 channel 分配给 10 个不同的 goroutine         chs[i] = make(chan int)          go Count(chs[i])     } // 5.在所有的 goroutine 启动完成后  	     for _, ch := range(chs) {          <-ch // 6.从 10 个 channel 中依次读取数据。在对应的 channel 写入数据前,这个操作也是阻塞的。     }}

这样,用 channel 实现了类似锁的功能,进而保证了所有 goroutine 完成后,主函数才返回。

channel 的声明形式:

var chanName chan ElementType

与一般的变量声明不同的地方仅仅是在类型之前加了 chan 关键字。ElementType 指定这个 channel 所能传递的元素类型。

var ch chan int // 声明一个传递类型为 int 的 channelvar m map[string] chan bool // 声明一个 map,元素是 bool 型的 channel

定义一个 channel,直接使用内置的函数 make():

ch := make(chan int) // 声明并初始化一个 int 型的 channel,名为 ch

channel 用法中,最常见的包括写入和读出。

ch <- value // 数据写入至 channel

向 channel 写入数据会导致程序阻塞,直到有其他 goroutine 从这个 channel 中读取数据。

value := <- ch

如果 channel 之前没有写入数据,那么从 channel 中读取数据也会导致程序阻塞,直到 channel 中被写入数据为止。

标签: #多进程并发的局限性是什么