您当前的位置:首页 >> 专题 >  
waitGroup源码解读原文
来源: 哔哩哔哩      时间:2023-08-07 23:56:38

WaitGroup定义:

state 是 WaitGroup 的核心,它是一个无符号的 64 位整型,并且用的是 atomic 包中的 Uint64,所以 state 本身是线程安全的。至于 为什么能保证线程安全,因为它使用了 CompareAndSwap(CAS) 操作,而这个操作依赖于 CPU 提供的原子性指令,是 CPU 级的原子操作。


(资料图片)

state 的高 32 位是计数器(counter),低 32 位是等待者数量(waiters)。其中计数器其实就是 Add(int) 数量的总和,譬如 Add(1) 后再 Add(2),那么这个计数器就是 1 + 2 = 3;而等待数量就是现在有多少 goroutine 在执行 Wait() 等待 WaitGroup 被释放。

信号量(Semaphore)是一种用于实现多进程或多线程之间同步和互斥的机制

信号量本质上可以简单理解为一个整型数,主要包含两种操作:P(Proberen,测试)操作和 V(Verhogen,增加)操作。其中,P 操作会尝试获取一个信号量,如果信号量的值大于 0,则将信号量的值减 1 并继续执行;否则,当前进程或线程就会被阻塞,直到有其他进程或线程释放这个信号量为止。V 操作则是释放一个信号量,将信号量的值加 1。

可以把信号量看作是一种类似锁的东西,P 操作相当于获取锁,而 V 操作相当于释放锁。由于信号量是一种操作系统级别的机制,通常由内核提供支持,因此我们不用担心上述对信号量的操作本身会产生竞态条件,相信内核能搞定这种东西。

-race 标志可以启用数据竞争检测器 (逻辑忽略)

`runtime_Semrelease` 和 `runtime_Semacquire` 是 Go 语言运行时的两个函数,用于实现协程之间的同步和通信。

`runtime_Semacquire` 函数实现了协程的等待操作。它接受一个 `*uint32` 类型的参数 `s`,表示一个信号量。当协程调用该函数时,如果信号量的值为 0,则该协程会被阻塞,直到其他协程调用了 `runtime_Semrelease` 函数将信号量的值增加为 1,才能继续执行。

`runtime_Semrelease` 函数实现了协程的通知操作。它接受一个 `*uint32` 类型的参数 `s`,表示一个信号量。当协程调用该函数时,会将信号量的值增加为 1,并唤醒因等待该信号量而被阻塞的协程。如果有多个协程正在等待该信号量,只会唤醒其中一个协程。

这两个函数的实现原理是基于操作系统提供的原子操作和阻塞操作。在 POSIX (可移植操作系统)系统中,可以使用 `sem_init`、`sem_wait` 和 `sem_post` 等函数来实现信号量的操作。在 Windows 系统中,可以使用 `CreateSemaphore`、`WaitForSingleObject` 和 `ReleaseSemaphore` 等函数来实现信号量的操作。

在 Go 语言中,由于需要支持跨平台运行,因此使用了一些平台无关的方法来实现信号量。具体来说,Go 语言运行时使用了一个名为 M:N 调度器的机制,其中 M 个用户级线程(goroutine)被映射到 N 个操作系统级线程上执行。在 M:N 调度器中,Go 语言运行时会维护一个全局的等待队列和一个全局的就绪队列,来实现协程之间的同步和通信。当一个协程调用 `runtime_Semacquire` 函数时,Go 语言运行时会将该协程加入到全局的等待队列中,并将其从当前线程上移除,然后阻塞该线程。当另一个协程调用 `runtime_Semrelease` 函数时,Go 语言运行时会将一个等待该信号量的协程从全局的等待队列中取出,并将其加入到全局的就绪队列中,然后唤醒一个空闲的线程来执行该协程。这样就实现了协程之间的同步和通信。

标签:

X 关闭

X 关闭