zoukankan      html  css  js  c++  java
  • Golang并行处理和内存模型

    GitHub_Note:Golang并行处理和内存模型

    go语言最好用的关键字:go, chan

    Processes and Threads

    • 进程:一个应用程序,一个为其所有资源(内存地址空间/设备/线程)而运行的容器。
    • 线程:一个进程从一个主线程开始,然后可以依次启动更多的线程,线程共享内存空间。

    Goroutine

    1. Create a Goroutine

    go关键字本质就是创建一个goroutine, 可以根据计算机核心来选择并行还是并发;

    main() 就是作为 goroutine 运行的。创建goroutine的例子:

    func f(){
        fmt.println(s)
    }
    
    func main(){
        s := "test"
        go f()
    }
    

    2. 销毁Goroutine

    var s string
    
    func main(){
        go func(){s = "test"}()
        fmt.println(s)
    }
    

    问题点: 没有用任何同步操作限制对s的赋值,因此其他的goroutine不一定哪呢个看到s的变化,需要用锁或channel这种同步机制来建立程序的执行顺序,即" goroutine"的并行化。

    3. Goroutine核心要点

    1. 生命周期管理:
    • 知道它什么时候结束:
    • 如何处理让它退出:如 http.Shutdown, context delay, chan发信号 ...
    1. 把并行的行为交给调用者:如先用函数包逻辑,在main()启用goroutine
    func main(){
        go func(){
            done <- serverApp()              // get exit info here
        }()
        for i := 0; i < cap(done); i++{
            <- done
            close(stop)                      // take it exit here
        }
    }
    
    func serverApp(stop chan struct{}) error{
        // goroutine1
        go func(){
            <- stop
            http.Shutdown()
        }()
        // caller goroutine2
        return http.Listen()
    }
    

    并行不是并发:

    • 并行:多个线程同时在不同的处理器执行单元运行(1个核心);
    • 并发:为多个线程在多个核心运行。

    channel

    channel通信是goroutine同步的主要方法。

    每一个在特定channel的发送操作都会匹配到通常在另一个goroutine执行的接收操作。

    在channel的发送操作先行发生于对应的接收操作完成:

    sync_channel.go

    var ch = make(chan int, 10)
    
    var a string
    
    func f(){
        a = "hello, world"
        ch <- 0
    }
    
    func main(){
        go f()
        fmt.Println(a)
    }
    

    保证"hello world"的成功print

    Lock 锁

    https://golang.org/pkg/sync/

    golang的sync实现了两个锁的数据类型:

    • sync.Mutex
    • sync.RWMutex

    Example: goroutine场景中,使每n次调用f()先行发生于第n+1次调用f()

    sync_mutex.go

    var l sync.Mutex
    var s string
    场景:cfg为包机全局对象,当很多goroutine同时访问时,存在data race,会看到不连续的内存输出。
    
    用go同步语义解决:
    - Mutex 互斥锁
    - RWMutex 读写锁
    - Atomic 原子锁
    
    
    func f() {
        s = "hello world"
        l.Unlock()
    }
    
    func main() {
        l.Lock()
        go f()
        l.Lock()
        fmt.Println(s)
    }
    

    Memory model

    Must read reference:

    阐述问题核心:Happen-Before 先行发生 多个Goroutine谁先谁后的问题, Example:

    # 以下两段代码,运行中若插入一个线程 x = 0
    # 代码源码, 输出为: 11111011111 ..
    x = 0
    for i in range(100):
        x = 1
        print(x)
    
    # 从硬件设计看,编译器优化重排会导致幺蛾子:
    x = 1
    for i in range(100):
        print(x)
    # 输出则可能为 11111100000 ..
    

    1. e1,e2两个事件同时发生,没有前后,我们认为这是并发行为
    2. 两个线程因为内存重排,由cache读取而不是到RAM,导致先行读到0输出。

    解决问题方案:memory barrier 内存屏障:即"锁"支持:

    要求所有对RAM的操作必须“扩散”到memory之后才能继续执行其他对memory的操作,我们可以用
    锁/原子/channel 或 更高级的锁 来处理(golang标准库可提供)。

    核心概念:

    memory barrier 表现为对一个变量v, "w -> r" 先写后读这个动作过程不受其他操作干扰:

    1. 读操作r看到最近一次的写操作w写入v的值
    2. 多个Goroutine共享v时,必须使用同步时间来建立Happen-Before

    做实验:
    go race command: go build -race xx.go
    Assembly processes: go tool compile -S xx.go

    package sync

    场景:cfg为包机全局对象,当很多goroutine同时访问时,存在data race,会看到不连续的内存输出。

    用go同步语义解决:

    • Mutex 互斥锁
    • RWMutex 读写锁
    • Atomic 原子锁
  • 相关阅读:
    python同时继承多个类且方法相同
    django扩展用户继承AbstractUser
    python中单下划线和双下划线
    django扩展用户一对一关联
    django拓展用户proxy代理
    django 内置User对象基本使用
    selenium+pthon之二----了解浏览器的相关操作方法
    最近很燥,决心沉下心来学习!
    selenium+pthon之一----环境搭建与脚本实例
    Fiddler入门三
  • 原文地址:https://www.cnblogs.com/kumata/p/14134763.html
Copyright © 2011-2022 走看看