首先理解同步调用与异步调用:

同步调用异步调用是两种常见的函数调用方式,尤其在多线程、并发编程中,这两种方式对程序的执行流程和效率有着直接影响。

1. 同步调用(Synchronous Call)

同步调用是指函数调用时,调用者会等待被调用的函数执行完毕并返回结果之后,才能继续执行后续操作。在同步调用过程中,函数的调用者会阻塞,直到函数执行完成。

特点

  • 阻塞:调用者会等待被调用的函数执行完成,才会继续执行后续代码。
  • 执行顺序:代码按顺序执行,当前任务完成后,才能继续下一个任务。

举个例子

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package main

import "fmt"
import "time"

// 模拟一个同步调用的函数
func longTask() {
time.Sleep(2 * time.Second)
fmt.Println("Long task finished")
}

func main() {
fmt.Println("Start task")
longTask() // 同步调用,调用者会等待该函数执行完
fmt.Println("End task")
}

输出:

plaintext
1
2
3
Start task
Long task finished
End task

在这个例子中,longTask() 函数执行时,程序会等待它执行完毕,然后才继续执行后面的 fmt.Println("End task")

2. 异步调用(Asynchronous Call)

异步调用是指调用者在调用函数时,不会等待函数执行完成,而是直接继续执行后续代码。异步调用通常会通过某种机制(比如回调函数、线程、goroutine等)来处理函数执行完成后的结果。

特点

  • 非阻塞:调用者不会等待被调用的函数执行完成,而是继续执行后续操作。
  • 执行顺序:调用者继续执行,而被调用的函数可能在后台执行,执行顺序不确定。

举个例子(使用 Go 的 goroutine)

go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package main

import "fmt"
import "time"

// 模拟一个异步调用的函数
func longTask() {
time.Sleep(2 * time.Second)
fmt.Println("Long task finished")
}

func main() {
fmt.Println("Start task")
go longTask() // 异步调用,goroutine 不会阻塞主线程
fmt.Println("End task")
time.Sleep(3 * time.Second) // 等待 goroutine 执行完毕
}

输出:

plaintext
1
2
3
Start task
End task
Long task finished

在这个例子中,longTask() 被异步调用(通过 go 关键字),主线程并不会等待 longTask() 执行完毕,而是继续执行 fmt.Println("End task")。由于 longTask() 是在一个新的 goroutine 中执行的,所以它会在主线程打印完 "End task" 后才完成。

同步调用与异步调用的区别

特性 同步调用 异步调用
执行方式 当前任务需要等待被调用函数执行完毕后才能继续 当前任务不会等待,被调用函数在后台执行
阻塞与非阻塞 阻塞:调用者会等待被调用函数完成 非阻塞:调用者不会等待被调用函数完成,继续执行
执行顺序 按顺序执行,当前任务完成后,继续下一个任务 执行顺序不确定,被调用函数可能稍后执行
适用场景 适用于需要等待函数执行完成后才能继续的情况 适用于可以并行处理,调用者不需要等待结果的情况
效率 效率较低,尤其在调用高延时操作时,调用者会被阻塞 效率较高,可以并行执行多个任务,不阻塞调用者

优缺点

  • 同步调用

    • 优点:简单,易于理解和调试,程序的执行顺序清晰。
    • 缺点:当操作耗时较长时,调用者需要等待,造成性能瓶颈。例如,I/O 密集型操作(如数据库查询、网络请求等)可能导致程序停滞。
  • 异步调用

    • 优点:提高并发性能,特别适合处理 I/O 密集型操作。调用者不需要等待操作完成,可以同时执行多个任务。
    • 缺点:需要更多的设计和管理,比如回调函数、并发控制等;调试和错误处理可能比较复杂。

何时选择同步和异步?

  • 同步调用:适用于需要按顺序执行任务且任务之间有依赖关系的场景。例如,计算任务需要按顺序完成,或者需要等待数据库查询结果再执行下一步操作。
  • 异步调用:适用于任务之间没有直接依赖关系,可以并行执行的场景。例如,网络请求、文件处理、并发计算等。

总结

  • 同步调用是阻塞的,调用者需要等待被调用函数执行完成后才能继续执行后续代码。
  • 异步调用是非阻塞的,调用者不会等待被调用函数执行完成,而是继续执行后续操作,通常通过回调或并发机制处理结果。

并发相关能力:

创建了一个异步的协程或者线程,如果你不知道它什么时候终止,最好不要创建它! 做到心中有数,理解并发!什么时候该用

线程: 是cpu需要执行的任务

并发执行,看似在一起做实行,如果是一个cpu核心那么:实则是一个人在两个任务之间快速的切换!(本质还是一个人做两件事,只不过两件事都分别分配一点时间)

两个核心执行两个线程 就相当于并行执行两个任务,时间将会被大大缩短

但是实际情况还是会非常复杂:

锁机制:

即使有两个cpu可以工作,但是他们需要持有锁的那个才可以工作!这也就导致了实际的工作还是并发执行!

高性能程序尽量避免使用锁

context之间形成多叉树的结构!

父ctx派生 多个子ctx

感知生命周期