table of Contents
Foreword
- Benpian notes on "Go practical language."
- Benpian major record some notes, or where different versions of gopl did not mention the
- This section is all about concurrency, more important, it took more effort
Complicated by
- Parallel is to allow different code segments simultaneously on different physical processor (CPU) performs on
- Parallel is doing many things at the same time
- Concurrency is manage a lot of things, these things can only do half of it was suspended do something else
Thread (thread) and process (process)
- When running an application, the operating system will launch a program for this process , this process includes a variety of application needs to use resources in operation and maintenance (including memory address space, file and device handles, threads , etc.), equivalent container
- A thread is an execution space, which the operating system will be scheduled (this is the OS thread scheduler, a distinction goroutine scheduler) to run the code written in the function
- Each process contains at least one thread , each process of the initial thread is called the main thread
- Since the main thread space is the application of space itself, so the main thread termination, the application will be terminated
- OS thread scheduler can schedule threads to a processor (CPU) running
- The operating system of physical processor (CPU) scheduler thread and the thread run
goroutine Overview
- Go language from concurrent synchronization model called CSP (Communicating Sequential Processes) paradigm (paradigm)
- Go concurrent language refers to the ability to make a function independent of other functions to run
- When you create a function goroutine, Go treats it as an independent unit of work
- This unit is scheduled (by the goroutine scheduler implemented) to an available logical processor executing
- goroutine scheduler can manage all goroutine be created and assigned execution time; the operating system can be a thread with language runtime logic processor binding, and logic processor running on goroutine
goroutine concurrency and parallelism
- The operating system in the physical processor scheduling a thread to run, while the Go language program is running in a logical processor scheduling on goroutine run
- Each logical processor are each bound to a single OS thread
- In version 1.5, Go will be for each available language runtime physical processor (CPU) is assigned a logical processor ; prior version 1.5, by default the entire application allocates only one logical processor
- If you create a goroutine and ready to run, this will be placed goroutine Go scheduler of global run queue , after which Go scheduler will this goroutine assigned to a logic processor, the logical processor to be placed in the corresponding local operating queue
- Local run queue goroutine will have to wait to know the assigned logical processor execution
- Logical processor is uniquely tied to the operating system thread
- When you need to perform a blocking system calls, running goroutine thread and will goroutine from logic processor separation, the thread will continue to clog, wait for the system to return calls
- At the same time, logical processors lose the thread to run, then Go scheduler creates a new thread , and the thread is bound to the logical processor
- 之后,Go调度器会从本地运行队列里选择另一个goroutine来运行
一旦被阻塞的系统调用返回,对应的goroutine会被放到本地运行队列,而之前的线程会保存好,以便后用
- Go调度器对可以创建的逻辑处理器的数量没有限制,但Go语言运行时默认限制每个程序最多创建10000个线程
- 可以通过runtime/debug包的SetMaxThreads方法来更改每个程序最多创建的线程数
- 如果希望goroutine并行,必须使用多于一个逻辑处理器。
- 当有多个逻辑处理器时,Go调度器会将goroutine平等分配到每个逻辑处理器上,也就是让goroutine在不同的线程上运行
不过要想真的实现并行的效果,用户需要让自己的程序运行在多个物理处理器(CPU)的机器上
概念小整理
- 上述有关并发的概念都加粗处理了
- 物理处理器,线程,进程,主线程,应用程序,操作系统线程调度器,Go调度器,逻辑处理器,全局运行队列,本地运行队列
- 笔者的小结:
1.当启动一个应用程序,操作系统会为其创建相应的进程,
2.进程拥有应用程序所需的各种资源,包括内存地址空间,线程等
3.每个进程至少有一个线程,每个进程的初始线程被称为主线程
4.线程是一个执行空间,而主线程的空间就是应用程序的空间,因此主线程终止,应用程序也会终止
5.操作系统线程调度器可以将线程调度到物理处理器(CPU)
6.物理处理器(CPU)调度线程并运行
7.对于Go语言1.5以上版本的程序(进程)而言,Go调度器默认会为每个可用物理处理器(CPU),分配一个逻辑处理器
8.创建一个goroutine并运行,这个goroutine会被放在Go调度器的全局运行队列
9.Go调度器负责把这些队列中goroutine分配给一个逻辑处理器,并且将这个逻辑处理器绑定到唯一的操作系统线程
10.逻辑处理器会将相应的goroutine放入本地运行队列
11.本地运行队列中的goroutine会一直等待直到自己被分配到逻辑处理器执行
12.当goroutine执行一个阻塞的系统调用,其对应线程会从逻辑处理器上分离,该线程被阻塞,等待系统调用的返回
13.Go调度器会为失去线程的逻辑处理器创建一个新线程,并将新线程绑定到该逻辑处理器上
14.系统调用执行完成并返回,对应的goroutine会放回到本地运行队列,而之前的线程会被保存后,以便后用
goroutine
- runtim包中的GOMAXPROCS(num)函数可以设置分配给Go调度器使用的逻辑处理器的数量
- runtime包中的NumCPU()可以获取可以使用的物理处理器数量
- sync包中WaitGroup类型,通过它的add方法可以计数,Done方法来减少计数,Wait方法来等待计数结束
竞争状态(race condition)
- 定义:如果两个或多个goroutine在没有相互同步的情况下,访问某个共享的资源,并试图同时读和写这个资源,就处于相互竞争的状态
- Go语言有一个工具可以在代码里检测竞争状态:go build -race
- gopl-zh的翻译者翻译成了竞争条件,反而不好理解
锁住共享资源(竞争状态的处理方法)
- sync/atomic包中原子函数
- sync包中的互斥锁(gopl还提到了读写锁)
- 通道方法:
无缓冲通道
有缓存通道
特别说明:当通道关闭后,goroutine依旧可以从通道接收数据,但是不能再向通道里发送数据
并发模式
runner
- runner包用于展示如何使用通道来监视程序的执行时间,如果程序运行时间太长,也可以用runner包来终止运行
- 笔者理解:使用Runner模式来执行一系列串行任务,可以设置运行的时间,还可以终止运行
- corn作业 :crontab命令常见于Unix和类Unix的操作系统之中,用于设置周期性被执行的指令。该命令从标准输入设备读取指令,并将其存放于“crontab”文件中,以供之后读取和执行。该词来源于希腊语chronos(χρόνος),原意是时间。通常,crontab储存的指令被守护进程激活,crond常常在后台运行,每一分钟检查是否有预定的作业需要执行。这类作业一般称为cron jobs。
pool
- pool包用于展示如何使用有缓冲的通道实现资源池,来管理可以在任意数量的goroutine之间共享及独立使用的资源
- Go1.5版本并没有pool包,而1.6及以后版本中,实现标准库sync.Pool资源池,推荐使用
work
- work包的目的是展示如何使用无缓冲的通道来创建一个goroutine池,这些goroutine执行并控制一组工作,让其并发执行
小结
- 可以使用通道来控制程序的生命周期
- 带default分之的select语句可以用来尝试向通道发送或者接收数据,而不会阻塞
- 有缓冲的通道可以用来管理一组可复用的资源
- 语言运行时会处理好通道的协作和同步
- 使用无缓冲的通道来创建完成工作的goroutine池
- 任何时间都可以用无缓冲的通道来让两个goroutine交换数据,在通道操作完成时一定保证对方接收到数据