go中的context
首先理解同步调用与异步调用:
同步调用和异步调用是两种常见的函数调用方式,尤其在多线程、并发编程中,这两种方式对程序的执行流程和效率有着直接影响。
1. 同步调用(Synchronous Call)
同步调用是指函数调用时,调用者会等待被调用的函数执行完毕并返回结果之后,才能继续执行后续操作。在同步调用过程中,函数的调用者会阻塞,直到函数执行完成。
特点:
- 阻塞:调用者会等待被调用的函数执行完成,才会继续执行后续代码。
- 执行顺序:代码按顺序执行,当前任务完成后,才能继续下一个任务。
举个例子:
1 | package main |
输出:
1 | Start task |
在这个例子中,longTask()
函数执行时,程序会等待它执行完毕,然后才继续执行后面的 fmt.Println("End task")
。
2. 异步调用(Asynchronous Call)
异步调用是指调用者在调用函数时,不会等待函数执行完成,而是直接继续执行后续代码。异步调用通常会通过某种机制(比如回调函数、线程、goroutine等)来处理函数执行完成后的结果。
特点:
- 非阻塞:调用者不会等待被调用的函数执行完成,而是继续执行后续操作。
- 执行顺序:调用者继续执行,而被调用的函数可能在后台执行,执行顺序不确定。
举个例子(使用 Go 的 goroutine):
1 | package main |
输出:
1 | Start task |
在这个例子中,longTask()
被异步调用(通过 go
关键字),主线程并不会等待 longTask()
执行完毕,而是继续执行 fmt.Println("End task")
。由于 longTask()
是在一个新的 goroutine 中执行的,所以它会在主线程打印完 "End task"
后才完成。
同步调用与异步调用的区别:
特性 | 同步调用 | 异步调用 |
---|---|---|
执行方式 | 当前任务需要等待被调用函数执行完毕后才能继续 | 当前任务不会等待,被调用函数在后台执行 |
阻塞与非阻塞 | 阻塞:调用者会等待被调用函数完成 | 非阻塞:调用者不会等待被调用函数完成,继续执行 |
执行顺序 | 按顺序执行,当前任务完成后,继续下一个任务 | 执行顺序不确定,被调用函数可能稍后执行 |
适用场景 | 适用于需要等待函数执行完成后才能继续的情况 | 适用于可以并行处理,调用者不需要等待结果的情况 |
效率 | 效率较低,尤其在调用高延时操作时,调用者会被阻塞 | 效率较高,可以并行执行多个任务,不阻塞调用者 |
优缺点:
同步调用:
- 优点:简单,易于理解和调试,程序的执行顺序清晰。
- 缺点:当操作耗时较长时,调用者需要等待,造成性能瓶颈。例如,I/O 密集型操作(如数据库查询、网络请求等)可能导致程序停滞。
异步调用:
- 优点:提高并发性能,特别适合处理 I/O 密集型操作。调用者不需要等待操作完成,可以同时执行多个任务。
- 缺点:需要更多的设计和管理,比如回调函数、并发控制等;调试和错误处理可能比较复杂。
何时选择同步和异步?
- 同步调用:适用于需要按顺序执行任务且任务之间有依赖关系的场景。例如,计算任务需要按顺序完成,或者需要等待数据库查询结果再执行下一步操作。
- 异步调用:适用于任务之间没有直接依赖关系,可以并行执行的场景。例如,网络请求、文件处理、并发计算等。
总结:
- 同步调用是阻塞的,调用者需要等待被调用函数执行完成后才能继续执行后续代码。
- 异步调用是非阻塞的,调用者不会等待被调用函数执行完成,而是继续执行后续操作,通常通过回调或并发机制处理结果。
并发相关能力:
创建了一个异步的协程或者线程,如果你不知道它什么时候终止,最好不要创建它! 做到心中有数,理解并发!什么时候该用
线程: 是cpu需要执行的任务
并发执行,看似在一起做实行,如果是一个cpu核心那么:实则是一个人在两个任务之间快速的切换!(本质还是一个人做两件事,只不过两件事都分别分配一点时间)
两个核心执行两个线程 就相当于并行执行两个任务,时间将会被大大缩短
但是实际情况还是会非常复杂:
锁机制:
即使有两个cpu可以工作,但是他们需要持有锁的那个才可以工作!这也就导致了实际的工作还是并发执行!
高性能程序尽量避免使用锁
context之间形成多叉树的结构!
父ctx派生 多个子ctx
感知生命周期