Golang进阶

Golang进阶_第1张图片

"白昼会边长,照亮心脏,让万物生长。"

一、Golang进阶

我们对golang的语法进行了一定的了解后,也算是入门了。本节的进阶篇围绕三个方向展开,Goroutine 、 Channel 、Sync。

如何理解并行与并发?

并行是指“并排行走”或“同时实行或实施”。
在操作系统中是指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)

并发,在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。

下面两张图就可以区别并发与并行;

并发:

Golang进阶_第2张图片

并行:

Golang进阶_第3张图片

同样,随着技术的迭代升级也有了并行+并发:

Golang进阶_第4张图片
看完上面的图解。
并发的实质是针对单核CPU,它的交替调度不同任务的能力
并行的是追是针对多核CPU,它指的是多个核心同时执行多个任务的能力。
由此,单核 CPU 只能并发,无法并行。并行,只存在于多核CPU的硬件条件下。
而在多核CPU中,并发并行都会同时存在,这是提高CPU处理任务能力的重要手段

(1)Goroutine

协程,又叫做轻量级线程。这在Golang这样的编程语言中特别流行。

Go协程的特点:
1.独立的栈空间
2.共享堆空间
3. 协程调度由用户控制(进程的控制是有操作系统控制,程序员不能控制)

不管是父进程创建的子进程,还是在linuxPOSIX提供的线程库。其中的管理都是交由操作系统。因此,在程序执行的过程中,总会存在执行状态的切换(用户到内核,内核到用户)。但是,golang中的协程完完全全解决了这个问题。

协程:用户态,轻量级线程。
线程:内核态,线程可以跑多个协程。
创建一个线程栈大概需要 1MB 左右,而协程栈大概只需要几 KB或者几十KB。
package main

import (
    "fmt"
    "time"
)

func hello(i int) {
    fmt.Println("go() to hello", i)
}

func HelloRotinue() {
    for i := 0; i < 5; i++ {
        go hello(i)
    }
    time.Sleep(time.Second)
}

func main() {
    HelloRotinue()
    for i := 0; i < 5; i++ {
        fmt.Println("main() to hello", i)
        time.Sleep(time.Second)
    }
}

我们在调用打印hello的函数前面加上关键字"go"。表面,我们起一个协程来调用这个函数。

Golang进阶_第5张图片

但我们此时没有设置HelloRotinue为线程,因此是等待该函数调用完时,才开始进行主线程main。

Golang进阶_第6张图片

此时,只需要在该函数前加上go,这两个执行流就不会处在所谓的"阻塞"状态,而是各管各的。

什么是CSP?

CSP理念: 以通信的方式来共享内存。Go 的并发哲学,依赖于 CSP 模型。

大多数编程语言,在如何实现并发的问题上,采用的都是基于线程和内存同步访问控制。而Go 的并发编程的模型则用 goroutine 和 channel 来替代。

Goroutine 和线程类似,channel 和 mutex (用于内存同步访问控制的互斥锁)类似。

我们在linux处着重讲了,实现进程间的本质,就是让每个进程(线程)可看到同一份公共资源。而这个而资源,也叫做临界区。

通过共享内存实现通信;

Golang进阶_第7张图片

通过通信共享内存;

Golang进阶_第8张图片

(2)Channel

make(chan+元素类型,[缓冲大小])
例如:
无缓冲通道 make(chan int)
有缓冲通道 make(chan int,2)

比如,现在我们要执行完成一个任务。协程1发送0~9数字,协程2拿到数字后计算它们的平方,并发送给主线程,并在主线程处打印。

Golang进阶_第9张图片

最后我们也可以得到结果。

Golang进阶_第10张图片

(3)Sync

sync(意指Synchronize,即“同步”)为UNIX操作系统的标准系统调用,功能为将内核文件系统缓冲区的所有数据。

sync:
Package sync provides basic synchronization primitives such as mutual exclusion locks. Other than the Once and WaitGroup types, most are intended for use by low-level library routines. Higher-level synchronization is better done via channels and communication.

包同步提供了基本的同步原语,如互斥锁。除了Once和WaitGroup类型之外,大多数类型都是供低级库例程使用的。更高级别的同步最好通过信道和通信来完成。

并发安全 Lock

我们期待五个协程对x进行2000循环地自增加1

package main

import (
    "fmt"
    "sync"
    "time"
)

var (
    x    int64
    lock sync.Mutex
)

func addWithLock() {
    for i := 0; i < 2000; i++ {
        lock.Lock()
        x += 1
        lock.Unlock()
    }
}

func addWithOutLock() {
    for i := 0; i < 2000; i++ {
        x += 1
    }
}

func main() {
    x = 0
    for i := 0; i < 5; i++ {
        go addWithOutLock()
    }
    fmt.Println("WithOutLock: ", x)
    time.Sleep(time.Second)

    x = 0
    for i := 0; i < 5; i++ {
        go addWithLock()
    }
    time.Sleep(time.Second)
    fmt.Println("WithLock: ", x)
}
Golang进阶_第11张图片

WaitGroup

Golang进阶_第12张图片

这个类里有三个比较重要的函数;

Golang进阶_第13张图片

我们简简单单地用五个协程分别打印。

Golang进阶_第14张图片

我们对代码进行一定的改进。让打印后的结果进行阻塞。

Golang进阶_第15张图片
Golang进阶_第16张图片

总结:

以上就是golang面向并发编程的相关介绍。本篇涉及的三个方面,协程(Gorotinue),信道(Channel)通过通信实现共享内存,sync关键字,支持同步互斥的并发安全操作。

本篇也就到此结束了~感谢你的阅读

祝你好运,向阳而生

Golang进阶_第17张图片

你可能感兴趣的:(Golang,golang,后端)