📌Golang📌基础📌S-并发.txt
进程和线程
	进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
	线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
	一个进程可以创建和撤销多个线程,同一个进程中的多个线程之间可以并发执行。

并发和并行
	多线程程序在一个核的cpu上运行,就是并发。
	多线程程序在多个核的cpu上运行,就是并行。
并发主要由切换时间片来实现"同时"运行,并行则是直接利用多核实现多线程的运行。

协程和线程
	协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
	线程:一个线程上可以跑多个协程,协程是轻量级的线程。

goroutine 只是由官方实现的超级"线程池"。
每个实例4~5KB的栈内存占用和由于实现机制而大幅减少的创建和销毁开销是go高并发的根本原因。
goroutine奉行通过通信来共享内存,而不是共享内存来通信。

只需在函数调用语句前添加go关键字,就可创建并发执行单元。
开发人员无需了解任何执行细节,调度器会自动将其安排到合适的系统线程上执行。
goroutine是一种非常轻量级的实现,可在单个进程里执行成千上万的并发任务。

调度器不能保证多个goroutine执行次序,且进程退出时不会等待它们结束。

Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。
可使用标准库函数runtime.GOMAXPROCS修改,让调度器用多个线程实现多核并行,而不仅仅是并发。

调用runtime.Goexit将立即终止当前goroutine执行,调度器确保所有已注册defer延迟调用被执行。
runtime.Gosched用于让出CPU时间片。

channel是CSP模式的具体实现,用于多个goroutine通讯。
其内部实现了同步,确保并发安全。多个goroutine同时访问,不需要加锁。

默认为同步模式,需要发送和接收配对。否则会被阻塞,直到另一方准备好后被唤醒。
异步方式通过判断缓冲区来决定是否阻塞。如果缓冲区已满,发送被阻塞;缓冲区为空,接收被阻塞。
通常情况下,异步channel可减少排队阻塞,具备更高的效率。但应该考虑使用指针规避大对象拷贝,将多个元素打包,减小缓冲区大小等。

可使用channel实现信号量。例如,使用两个goroutine轮流打印0~9:
	ch1 := make(chan struct{}, 1)
	ch2 := make(chan struct{}, 1)
	go func() {
		for i := 0; i < 9; i += 2 {
			<-ch1
			println("goroutine A:", i)
			ch2 <- struct{}{}
		}
	}()
	go func() {
		for i := 1; i <= 9; i += 2 {
			<-ch2
			println("goroutine B:", i)
			ch1 <- struct{}{}
		}
	}()
	ch1 <- struct{}{}
	time.Sleep(2 * time.Second)

closed-channel不会阻塞,因此可用作退出通知。
	stop := make(chan struct{})
	go func() {
		time.Sleep(time.Second)
		log.Println("ok")
		close(stop) //发起退出通知
	}()
	<-stop //等待退出通知
	log.Println("exit")

channel配合signal.Notify监听退出信号可以实现程序的优雅退出。
func Notify() {
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) //监听退出信号
	<-quit //阻塞进程直到捕捉到退出信号
}
	Notify() //等待退出通知
	//通知子协程退出
	//等待子协程退出

WaitGroup总共有三个方法:Add(delta int),Done(),Wait()。
	Add:添加或者减少等待goroutine的数量,应在创建新的线程或者其他应等待的事件之前调用。
	Done:相当于Add(-1),应在线程的最后执行。
	Wait:阻塞直到WaitGroup计数器减为0。

WaitGroup的用途:它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成。
	wg := &sync.WaitGroup{}
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(n int) {
			time.Sleep(time.Second)
			log.Println(n)
			wg.Done()
		}(i)
	}
	wg.Wait() //阻塞主线程的执行
	log.Println("exit")

Context包专门用来简化对于处理单个请求的多个goroutine之间与请求域的数据,取消信号,截止时间等相关操作。

使用时遵循context规则:
	不要将Context放入结构体,Context应该作为第一个参数传入,命名为ctx。
	即使函数允许,也不要传入nil的Context。如果不知道用哪种Context,可以使用context.TODO()。
	使用context的Value相关方法,只应该用于在程序和接口中传递和请求相关数据,不能用它来传递一些可选的参数
	相同的Context可以传递给在不同的goroutine;Context是并发安全的。

context包类型
	type Context interface {
		Deadline() (deadline time.Time, ok bool) //deadline是当前Context的应该结束的时间,ok表示是否有deadline
		Done() <-chan struct{} //返回一个struct{}类型的只读channel,当Context被撤销或过期时,该信道是关闭的
		Err() error //当Done信道关闭后,Err方法表明Context被撤的原因
		Value(key any) any //Value可以让Goroutine共享一些数据,当然获得数据是协程安全的。
	}
	type CancelFunc func()
	type CancelCauseFunc func(cause error)
context包定义错误:
	Canceled DeadlineExceeded
context包类型和方法:
	func Background() Context
	func TODO() Context
	type CancelFunc func()
	type CancelCauseFunc func(cause error)
	func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
	func WithCancelCause(parent Context) (ctx Context, cancel CancelCauseFunc)
	func WithoutCancel(parent Context) Context
	func Cause(c Context) error
	func AfterFunc(ctx Context, f func()) (stop func() bool)
	func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
	func WithDeadlineCause(parent Context, d time.Time, cause error) (Context, CancelFunc)
	func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
	func WithTimeoutCause(parent Context, timeout time.Duration, cause error) (Context, CancelFunc)
	func WithValue(parent Context, key, val any) Context

context通知退出
	ctx, cancel := context.WithCancel(context.Background())
	go func() {
		time.Sleep(time.Second)
		log.Println("ok")
		cancel() //发起退出通知
	}()
	<-ctx.Done() //等待退出通知
	log.Println("exit")

========== ========== 监听信号并通知协程退出 ========== ==========

示例一:
	stop := make(chan struct{})
	done := make(chan struct{})
	//ctx, cancel := context.WithCancel(context.Background())
	go func() {
		tk := time.NewTicker(3 * time.Second)
		for {
			select {
			case <-stop:
				log.Println("closing ...")
				tk.Stop()
				close(done)
				//cancel()
				return
			case <-tk.C:
				log.Println("do task")
			}
		}
	}()
	Notify()
	close(stop) //可使用channel或context控制退出
	<-done      //单个协程可使用channel或context阻塞等待
	//<-ctx.Done()
	log.Println("exit")

示例二:
	ctx, cancel := context.WithCancel(context.Background())
	wg := sync.WaitGroup{}
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go func(n int) {
			for {
				select {
				case <-ctx.Done():
					log.Println(n, "closing...")
					wg.Done()
					return
				default:
					log.Println(n, "do task")
					time.Sleep(time.Second)
				}
			}
		}(i)
	}
	Nofity()
	cancel()  //可使用channel或context通知协程退出
	wg.Wait() //多个协程使用WaitGroup阻塞等待
	log.Println("exit")