并发x并行x串行 并发: 并发是一个核运行多线程程序,说人话就是一个程序分为好多个子程序,然后一个人干,这个人一会干这个一会干那个,类似于ABCBCABCA….这样子干,因为计算机运行速度很快,就给人一种“多线程”的感觉,一个人在同时做很多事情
并行: 并行就是多核运行多线程程序,说人话就是多个人一起干多个程序
串行: 多线程程序中,要先干完线程A,再干线程B……
并发就是一个人同时吃三个馒头,并行是三个人吃三个馒头
在Go实战中描述
并行是让不同的代码片段在不同的物理处理器上执行,关键是同时 做很多事情
并发是指同时管理 很多事情,这些事情可能做了一半就被暂停就去做别的事情了
一般来说,我们总觉得并行更好,因为他多步进行,但是事实上操作系统上的资源总是少得可怜,从”使用较少的资源做更多的事情”的哲学出发,并发只使用了很少的资源,却能够管理很多事情,注意这里是管理。
线程和协程 线程: 内核态,线程跑多个协程,栈MB级别
协程: 用户态,轻量级线程,栈KB级别
没啥好理解的,反正协程就是轻量
1 2 3 4 5 6 7 8 9 10 func hello (i int ) { println ("hello goroutine :" + fmt.Sprint(i)) } func HelloGoRoutine () { for i := 0 ; i < 5 ; i++ { go hello(i) } time.Sleep(time.Second * 1 ) }
1 2 3 4 5 6 print :hello goroutine: 4 hello goroutine: 3 hello goroutine: 0 hello goroutine: 2 hello goroutine: 1
再次理解一下 进程 = ==内存== + ==文件和设备的句柄== + ==CPU调度器==(线程)
每个进程至少包含一个线程,每个进程初始线程被称作主线程,因此我们常常会使用sync.wait来使主goroutine来等待其他协程的完成,如果主goroutine先关闭,其他协程也会关闭
进程是一个大的空间,包含程序执行的各种资源
线程是一个执行 的空间
多线程——多CPU
而一个线程 上有一个==逻辑处理器==(Go特有),而这个逻辑处理器处理的就是协程
我们可以把这个逻辑处理器看作一个队列,而开启一些goroutine就是让这些goroutine入队,逻辑的含义就是系统会通过一些算法使得多个goroutine按顺序运行,同时需要明白的是:运行顺序并不是goroutineA,goroutineB… 而是会在某个goroutine运行到某个阶段去运行其他goroutine,具体运行到什么阶段 就是算法的事情了
竞争状态 如果多个或者多个 goroutine 在没有互相同步的情况下,访问某个共享 的资源,并试图同时读和写这个资源,就会处于相互竞争的状态。
这个状态我们可以从Mysql的幻读等缺陷可以类比
因此我们需要锁住共享资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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 Add () { x = 0 for i := 0 ; i < 5 ; i++ { go addWithoutLock() } time.Sleep(time.Second) println ("WithoutLock:" , x) x = 0 for i := 0 ; i < 5 ; i++ { go addWithLock() } time.Sleep(time.Second) println ("WithLock:" , x) }
1 2 3 print :goroutineWithLock 10000 goroutineWithoutLock 8156
atomic包 这个包的名字就是原子函数,从原子性就可以知道他的作用是什么
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 func main () { w.Add(2 ) go incCounter(1 ) go incCounter(2 ) w.Wait() fmt.Println("Final Counter:" , counter) } func incCounter (id int ) { defer w.Done() for count := 0 ; count < 2 ; count++ { atomic.AddInt64(&counter, 1 ) runtime.Gosched() } }
==LoadInt64== 和 ==StoreInt64== 提供了读和写的方式,可以试试
time.S
leep()太呆板了,换个高级的sync.WaitGroup
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 var ( x int64 lock sync.Mutex wg sync.WaitGroup ) func addWithLock () { defer wg.Done() for i := 0 ; i < 2000 ; i++ { lock.Lock() x = x + 1 lock.Unlock() } } func main () { x = 0 wg.Add(5 ) for i := 0 ; i < 5 ; i++ { go addWithLock() } wg.Wait() fmt.Println("addWithLock:" , x) }
1 2 print :addWithLock: 10000
Channel 请跳转到Channel那篇笔记中