一、goroutine 池
- 本質上是生產者消費者模型
- 在工作中我們通常會使用可以指定啟動的 goroutine 數量-worker pool 模式,控制 goroutine 的數量,防止 goroutine 泄漏和暴漲
- 一個簡易的 work pool 示例代碼如下:
package mainimport ("fmt""time"
)func worker (id int, jobs <-chan int, results chan <- int) {//消費者消費任務for j := range jobs {fmt.Printf("worker:%d start job:%d\n", id, j)time.Sleep(time.Second)fmt.Printf("worker:%d end job:%d\n", id, j)results <- j * 2}}func main() {jobs := make(chan int, 100)results := make(chan int, 100)// 1)開啟3個goroutine,作為消費者消費 jobs中任務for w := 1; w <= 3; w++ {go worker(w, jobs, results)}// 2)5個任務(生產者生產任務)for j := 1; j <= 5; j++ {jobs <- j}close(jobs)// 3)輸出結果for a := 1; a <= 5; a++ {v := <-resultsfmt.Println(v)}
}
二、打印奇數偶數
1、一個無緩沖管道實現
- 首先我們這里通過 make(chan int),開辟的通道是一種無緩沖通道
- 所以當對這個緩沖通道寫的時候,會一直阻塞等到某個協程對這個緩沖通道讀
- 而這里我講 ch <- true 理解為色號給你吃,它卻是需要等到某個協程讀了才能繼續運行
package mainimport ("fmt""sync"
)var wg sync.WaitGroupfunc printJS(ch chan bool) {defer wg.Done()for i := 1; i <= 9; i += 2 {fmt.Println("js", i) // 奇數先打印ch <- true // 給偶數打印函數一個信號(需要等到某個協程讀了再能繼續運行)<-ch}
}func printOS(ch chan bool) {defer wg.Done()for i := 2; i <= 10; i += 2 {<-ch // 偶數等待奇數函數 向chan發送信號fmt.Println("os", i)ch <- false // 給奇數打印函數一個信號(需要等到某個協程讀了再能繼續運行)}
}func main() {// 新建一個無緩沖管道(無緩沖管道只能一個協程寫入,然后另外一個協程來讀取)ch := make(chan bool)wg.Add(2)go printJS(ch)go printOS(ch)wg.Wait()
}
2、兩個無緩沖管道實現
package mainimport ("fmt""sync"
)var ch1 = make(chan bool)
var ch2 = make(chan bool)
var wg sync.WaitGroupfunc go1JS() {defer wg.Done()for i := 1; i <= 10; i += 2 {<-ch1 // ch1獲取數據成功就不阻塞,進行下一步fmt.Println(i)ch2 <- true // 向ch2發送信號,打印奇數}<-ch1 // 因為main函數最初向ch1放入了一個數據,所以最后打印結束后取出,否則死鎖
}
func go2OS() {defer wg.Done()for i := 2; i <= 10; i += 2 {<-ch2fmt.Println(i)ch1 <- true}
}
func main() {wg.Add(2)go go1JS() // 打印奇數go go2OS() // 打印偶數ch1 <- true // 先讓奇數的協程執行wg.Wait()
}
三、超時控制
1、基礎版
package mainimport ("fmt""math/rand""time"
)// 在 main 函數里調用給定的 rpc 方法,并設置超時時間為 10 秒
// 在等待過程中如果超時則取消等待并打印 "timeout" ,如果沒有超時則打印出 rpc 的返回結果。
// rpc 方法不可以修改
func main() {ch := make(chan bool)var ret intgo func() {ret = rpc()<-ch}()count := 0for count < 10 {if ret != 0 {fmt.Println(ret)break}time.Sleep(time.Second)count += 1}if count >= 10 {ch <- falsefmt.Println("timeout")}
}// 這是你要調用的方法,可以看作一個黑盒
// 它的耗時是 1~15 秒內的隨機數
// 最終返回一個隨機的 int 類型
func rpc() int {cost := rand.Intn(15) + 1fmt.Printf("rpc will cost %d seconds\n", cost)time.Sleep(time.Duration(cost) * time.Second)return cost
}func init() {rand.Seed(time.Now().UnixNano())
}
2、time.After控制超時
package mainimport ("fmt""time"
)func main() {workDoneCh := make(chan bool, 1)go func() {LongTimeWork() //這是我們要控制超時的函數workDoneCh <- true // 函數正常執行結束給 chan信號正常退出}()select {case <-workDoneCh: // 當協程執行完成后會向這個 channel 發送一個數據,收到即可結束fmt.Println("Success!")case <-time.After(3 * time.Second): //timeout到來fmt.Println("timeout") // 3s無返回超時退出}
}func LongTimeWork() {time.Sleep(time.Second * 2)
}