Go 并发实战--协程浅析 二

前言

继续上一篇的内容,我们介绍了go协程的实现中的几个核心的对象,也说了他们之间是如何合作工作的。


image.png

下面来看一些go协程的使用:

    go func() {
    fmt.Println("hello goroutine")
    }()

这样就完成了一个最简单的demo,相较而言go协程的使用同Java中线程的使用要简单太多了,至少少写了十几行有没有,也更加清晰了。

并发编程模型

就并发来说,通常分为Actor模型CSP(communicating sequential processes)模型多线程共享内存并发这么几种
多线程共享内存:
并发是大多数语言中的实现,比如说Java、C++、python等。这种方式基本上是最原始的并发编程的形式,我们需要通过锁来协同各个线程,保证并发的安全性。在这个基础上语言提供了&我们也实现了不少的并发工具和线程安全的数据结构,比如说ConcurrentHashMap、CountDownLatch、ReentrantLock等,这种编程模式对于程序员要求较高,需要非常清楚所使用API的各个细节及线程&内存实现,平心而论学习成本&门槛较高。
Actor模型:
由于多线程共享内存的编程模式,编程复杂度较高,并且容易出问题,调试起来更加的费劲,所以产生了像是Actor、CSP这些并发模型。
actor模型是处理并行计算的概念模型,它定义了系统部件行为和交互的一些规则,Actor模型内部的状态由它自己维护即它内部数据只能由它自己修改(通过消息传递来进行状态修改),所以使用Actors模型进行并发编程可以很好地避免这些问题,Actor由状态(state)、行为(Behavior)和邮箱(mailBox)三部分组成。实现比较经典的有Erlang,Java中也有对应的实现。
actor有这么几个好处:
1、事件模型驱动--Actor之间的通信是异步的
2、强隔离性--Actor中的方法不能由外部直接调用,所有的一切都通过消息传递进行的,从而避免了Actor之间的数据共享。
3、位置透明--无论Actor地址是在本地还是在远程机上对于代码来说都是一样的。
4、轻量性--Actor是非常轻量的计算单元,单个Actor仅占400多字节,只需少量内存就能达到高并发。
缺点:
CSP 模型:
CSP的核心理念是通过消息传递的尝试交互而不是共享内存,关注点更是仅关心消息的传递方式,而不是关心发送方和接收方。Actor也是通过消息来进行通信的,但是csp和actor的差异在于CSP进程通常是同步的(即任务被推送进Channel就立即执行,如果任务执行的线程正忙,则发送者就暂时无法推送新任务),Actor进程通常是异步的(消息传递给Actor后并不一定马上执行)。Actor 更加关注的是一个数据和行为封闭。
go支持共享内存这种比较原始的方式,也支持CSP模型,go的官方建议是强力建议使用消息传递也就是CSP的模式完成并发的构建,原话是这么说的:
Do not communicate by sharing memory; instead, share memory by communicating.
“不要以共享内存的方式来通信,相反,要通过通信来共享内存。”
go具有天然的并发性,为了保证在这个优势的基础上构建更加安全稳定的应用,go提出了这些建议。
我们在使用go写成的时候也应该尽可能的遵循这些建议,关于管道的使用可以看channel那篇文章,后面也会有相应的实战。
具体来说是这样的,下面来看一个很经典的生产者消费者的例子。

package main

import "fmt"

func main() {
    var channel = make(chan int, 3) // 用于消息通信的管道
    var c2 = make(chan int) // 防止main提前退出的通道
    // 同时开启两个go协程创建生产者消费者,能大致模拟同步进行
    go func() { // 开一个协程用来启动生产者
        for i:=0; i<100 ; i++ {
            go produce(channel, i)
        }
    }()
    go func() { // 开一个协程用来启动消费者
        for i:=0; i<100 ; i++ {
            go consumer(channel)
        }
    }()
    
    <-c2 // 防止主线程直接结束
}
func produce(channel chan int, data int) {
    channel <- data
}
func consumer(channel chan int) {
    fmt.Println(<- channel)
}

解释一下上述的代码,producer通过channel向生产者发送产品,而consumer从channel来取产品,producer&consumer并没通过一个共享内存直接交互,而是通过这种发消息的方式来进行协同的,包括阻塞等待等。
当然了,我们也可以用共享内存这种方式来完成并发编程,go也提供了对应的API:

    sync.Mutex.Lock()
    if i >2 {
       i = i + 1
     } else {
       i = i + 2
     }
    sync.Mutex.Unlock()

像上述代码,就通过sync.Mutex的锁操作,完成了共享变量i的一个存在竞态条件的操作。
剩下的go协程相关的就从后面并发编程的几个经典场景来理解吧,本篇暂时就先讲这么多。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • Go 并发编程 选择 Go 编程的原因可能是看中它简单且强大,那么你其实可以选择C语言;除此之外,我看中 Go 的...
    PRE_ZHY阅读 977评论 1 6
  • 最近我们钻石小区大门口安装了一套汽车号牌识别系统,也就是说把汽车号牌输入系统里,只要是你的汽车出入小区,电子屏幕上...
    陆学峰阅读 1,699评论 6 10
  • 何处缕幽香,缘是栀子绕。 忆却儿时烂漫岗,朵朵相窈窕。 楼外响轻雷,闲雨听年少。 花落花开几度回? 故梦尘中老。
    淡月轩阅读 559评论 5 7
  • 1、我怎么如此幸运:凌晨醒来打开QQ空间豁然发现,自己之前从曾经写了那么多的文章,有一大大半是关于思维导图应用方...
    大漠先生工作室阅读 272评论 0 3
  • node基本概念 什么是node? node源于js的流行,因而出现性能极强的V8。Node是JavaScript...
    醉饮满天星阅读 1,102评论 0 1

友情链接更多精彩内容