首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 并发实战--协程浅析 二

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

作者头像
邹志全
发布2019-07-31 11:00:23
2780
发布2019-07-31 11:00:23
举报
文章被收录于专栏:EffectiveCodingEffectiveCoding
前言

继续上一篇的内容,我们介绍了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协程相关的就从后面并发编程的几个经典场景来理解吧,本篇暂时就先讲这么多。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019.07.10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 并发编程模型
相关产品与服务
GPU 云服务器
GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档