Golang非CSP并发模型外的其他并行方法总结

栏目: Go · 发布时间: 5年前

内容简介:Golang最为让人熟知的并发模型当属CSP并发模型,也就是由goroutine和channel构成的GMP并发模型,具体内容不在赘述了,可以翻回之前的文章查看。在这里,要讲讲Golang的其他并发方式。Golang不仅可以使用CSP并发模式,还可以使用传统的共享数据的并发模式。这是传统语言比较常用的的方式,即加锁。加锁使其线程同步,每次只允许一个goroutine进入某个代码块,此代码块区域称之为”

Golang最为让人熟知的并发模型当属CSP并发模型,也就是由goroutine和channel构成的GMP并发模型,具体内容不在赘述了,可以翻回之前的文章查看。在这里,要讲讲Golang的其他并发方式。

Golang不仅可以使用CSP并发模式,还可以使用传统的共享数据的并发模式。

临界区(critical section)

这是传统语言比较常用的的方式,即加锁。加锁使其线程同步,每次只允许一个goroutine进入某个代码块,此代码块区域称之为” 临界区(critical section) ”。

Golang为 临界区(critical section) 提供的是互斥锁的包和条件变量的包。

互斥锁

就是通常使用的锁,用来让线程串行用的。Golang提供了互斥锁 sync.Mutex 和读写互斥锁 sync.RWMutex ,用法极其简单:

var s sync.Mutex
    
s.Lock()
    
// 这里的代码就是串行了,吼吼吼。。。
    
s.Unlock()

Lock和Unlock

sync.Mutexsync.RWMutex 的区别

没啥大的区别,只不过 sync.RWMutex 更加细腻,可以将“读操作”和“写操作”区别对待。

sync.RWMutex 中的Lock和unLock针对写操作

var s sync.RWMutex

s.Lock()

// 上写锁了,吼吼

s.Unlock()

sync.RWMutex 中的RLock和RUnLock针对读操作

var s sync.RWMutex

s.RLock()

// 上读锁了,吼吼..

s.RUnlock()

读写锁有以下规则:

  • 写锁被锁定,(再试图进行)读锁和写锁都阻塞
  • 读锁被锁定,(再试图进行)写锁阻塞,(再试图进行)读锁不阻塞

即:多个写操作不能同时进行,写操作和读操作也不能同时进行,多个读操作可以同时进行

注意事项:

  • 不要重复锁定互斥锁;因为代码写起来麻烦,容易出错,万一死锁(deadlock)了就废了。Go语言运行时系统自己抛出的panic都属于致命错误,都是无法恢复的,调用 recover 函数对它们起不到任何作用。一旦产生死锁,程序必然崩溃。
  • 锁定和解锁一定要成对出现,如果怕忘记解锁,最好是使用 defer 语句来解锁;但是,一定不要对未锁定的或者已经锁定的互斥锁解锁,因为会触发 panic ,而且此 panic 和死锁一样,属于致命错误,程序肯定崩溃
  • sync.Mutex 是个结构体,尽量不要其当做参数,在多个函数直接传播。因为没啥意义,Golang的参数都是副本,多个副本之间都是相互独立的。

条件变量Cond

互斥锁是用来锁住资源,“创造”临界区的。而条件变量Cond可以认为是用来自行调度线程(在此即为groutine)的,当某个状态时,阻塞等待,当状态改变时,唤醒。

Cond的使用,离不开互斥锁,即离不开 sync.Mutexsync.RWMutex

Cond初始化都需要有个互斥锁。(ps:哪怕初始化不需要,就应用场景而言,也得需要个互斥锁)

Cond 提供Wait、Signal、Broadcast 三种方法。

Wait表示线程(groutine)阻塞等待;

Signal表示唤醒等待的groutine;

Broadcast表示唤醒等待的所有groutine;

初始化:

cond := sync.NewCond(&sync.Mutex{})

在其中一个groutine中:

cond.L.Lock()
for status == 0 {
     cond.Wait()
}
//状态改变,goroutine被唤醒后,干点啥。。。
cond.L.Unlock()

以上算是模板

在另外一个groutine中:

cond.L.Lock()
status = 1
cond.Signal() // 或者使用cond.Broadcast()来唤醒以上groutine中沉睡的groutine
cond.L.Unlock()

原子操作(atomicity)

原子操作是硬件芯片级别的支持,所以可以保证绝对的线程安全。而且执行效率比其他方式要高出好几个数量级。

Go语言的原子操作当然也是基于CPU和操作系统的,Go语言提供的原子操作的包是 sync/atomic ,此包提供了加(Add)、CAS(交换并比较 compare and swap)、成对出现的存储(store)和加载(load)以及交换(swap)。

此包提供的大多数函数针对的数据类型也非常的单一:只有整型!使用方式十分的简单,看着函数直接调用就好。

var a int32
a = 1
a = atomic.AddInt32(&a, 2) //此处是原子操作,就这么简单,吼吼

在此特别强调一下CAS,CAS对应的函数前缀是“CompareAndSwap”,含义和用法正如英文翻译:比较并交换。在进行CAS操作的时候,函数会先判断被操作变量的当前值是否与我们预期的旧值相等,如果相等,它就把新值赋给该变量,并返回true,反之,就忽略此操作,并返回false。

可能是Golang提供的原子操作的数据类型实在是有限,Go又补充了一个结构体 atomic.Value ,此结构体相当于一个小容器,可以提供原子操作的存储 store 和提取 load

var atomicVal atomic.Value
str := "hello"

atomicVal.Store(str) //此处是原子操作哦

newStr := atomicVal.Load() //此处是原子操作哦

其他

为了能更好的调度goroutine,Go提供了 sync.WaitGroupsync.Once 两个包。

sync.WaitGroup

sync.WaitGroup 的作用就是在多goroutine并发程序中,让主goroutine等待所有goroutine执行结束。(直接查看代码注释)

sync.WaitGroup 提供了三个函数 AddDoneWait 三者用法如下:

  • Add 写在主goroutine中,参数为将要运行的goroutine的数量
  • Done 写在各个非主goroutine中,表示运行结束
  • Wait 写在主goroutine中,block主goroutine,等待所有其他goroutine运行结束
var wait sync.WaitGroup

    wait.Add(2) //必须是运行的goroutine的数量

    go func() {
        //TODO 一顿小操作
        defer wait.Done() // done函数用在goroutine中,表示goroutine操作结束
    }()

    go func() {
        //TODO 一顿小操作
        defer wait.Done() // done函数用在goroutine中,表示goroutine操作结束
    }()

    wait.Wait() // block住了,直到所有goroutine都结束

注意

sync.WaitGroup 中有一个计数器,记录的是需要等待的goroutine的数量,默认值是0,可以通过Add方法来增加或者减少值,但是切记,千万不能让计数器的值小于零,会触发panic!

sync.WaitGroup 调用Wait方法的时候, sync.WaitGroup 中计数器的值一定要为0。因此Add中的值一定要等于非主goroutine的数量!

且不要把Add和Wait方法放到不同的goroutine中执行!

sync.Once

真真正正的只执行一次。

sync.Once 只要一个方法: Do ,里面就一个参数: func 。多说无益,复制下面代码,猜猜执行结果就知道了。

package main

import (
    "fmt"
    "sync"
)

func main() {
    var once sync.Once
    onceBody := func() {
        fmt.Println("Only once")
    }
    done := make(chan bool)
    for i := 0; i < 10; i++ {
        go func() {
            once.Do(onceBody)
            done <- true
        }()
    }
    for i := 0; i < 10; i++ {
        <-done
    }
}

执行结果

Only once

没错,只有一行。真只执行了一次。


以上所述就是小编给大家介绍的《Golang非CSP并发模型外的其他并行方法总结》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

能量分析攻击

能量分析攻击

Stefan Mangard、Elisabeth Oswald、Thomas Popp / 科学出版社 / 2010-8 / 58.00元

《能量分析攻击》可以作为密码学、电子工程、信息安全等专业的教材,也可以供相关专业人员参考。能量分析攻击旨在通过分析密码设备的能量消耗这一物理特性来恢复设备内部的秘密信息,这种基于实现特性的密码分析对广泛应用的各类密码模块的实际安全性造成了严重威胁,《能量分析攻击》是关于能量分析攻击的综合性专著,系统阐述了能量分析攻击的基本原理、技术方法以及防御对策的设计与分析。一起来看看 《能量分析攻击》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具