1.协程的概念
Go 应用程序并发处理的部分被称作 goroutines(协程)
,可以进行更有效的并发运算。协程是抽象出来的线程,并在用户空间进行协程管理,减少内核的压力。

2.实战
2.1 协程的实战
协程是通过使用关键字 go
调用一个函数或者方法来实现的,即在当前的计算过程中开始一个同时进行的函数,在相同的地址空间中并且分配了独立的栈,例如:go sum(bigArray)
package goroutine
import (
"fmt"
"time"
)
func longWait() {
fmt.Println("Beginning longWait()")
time.Sleep(5 * 1e9) // sleep for 5 seconds
fmt.Println("End of longWait()")
}
func shortWait() {
fmt.Println("Beginning shortWait()")
time.Sleep(2 * 1e9) // sleep for 2 seconds
fmt.Println("End of shortWait()")
}
package goroutine
import (
"fmt"
"testing"
"time"
)
func TestGoroutine(t *testing.T) {
fmt.Println("In goroutine()")
go longWait()
go shortWait()
fmt.Println("About to sleep in goroutine()")
// sleep works with a Duration in nanoseconds (ns) !
time.Sleep(10 * 1e9)
fmt.Println("At the end of goroutine()")
}
2.2 互斥锁的实战
如果使用了互斥锁,可以保证每次进入临界区的只有一个 goroutine,一个 goroutine 执行完后,另一个 goroutine 才能进入临界区执行,最终就实现了并发控制。

package goroutine
import (
"fmt"
"sync"
"testing"
)
func TestGoroutineWg(t *testing.T) {
var lock sync.Mutex
var count int
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
lock.Lock()
defer lock.Unlock()
count++
fmt.Println("count1=", count)
}()
wg.Add(1)
go func() {
defer wg.Done()
lock.Lock()
defer lock.Unlock()
count--
fmt.Println("count2=", count)
}()
wg.Wait()
fmt.Println("count3=", count)
}
type WaitGroup struct {
noCopy noCopy
state1 [3]uint32 #代表三个字段:counter, waiter, sema
}
● counter:计数器,每次经过wg.Add(X)或者wg.Done()后的值
● waiter:调用wg.Wait()的数量,也就是等待者的数量
● sema:信号量,用于换醒Wait()函数
退出 WaitGroup 的条件:counter == 0
Add(n):
① 更新 counter 的值。counter += n
② 判断当前 counter > 0 || waiter == 0,满足条件,说明当前还有groutine没有执行完,直接返回。
③ 走到这一步,说明 counter == 0 && waiter != 0,说明groutine全部执行了 Done()
方法,换醒执行了wg.Wait()的协程,将state设置成0,返回。
Wait():
① 判断 counter == 0,如果为 true,说明 groutine 已经全部执行了 Done() 方法,Wait Done。
② 此时 counter != 0,说明需要阻塞当前协程的执行,执行下面的 CAS 判断
③ CAS 判断 wg.state 有没有被改变,没有则更新 wg.state 为1,当前协程进入 sleep 状态,等待信号量换醒。换醒后继续循环判断,此时 counter==0,直接 wait done。
④ CAS判断 wg.state 有没有被改变,有则继续循环判断。继续判断 counter
Done():
Done 的实现就是 Add. 只不过我们常规用法 wg.Add(1) 是加 1 ,wg.Done() 是减 1,即 wg.Done() 可以用 wg.Add(-1) 来代替。
网友评论