Add Chapter 5: Concurrency - Goroutines, Channels, Sync Primitives, and Patterns (Deep Dive)
This commit is contained in:
371
chapters/chapter-5/main.go
Normal file
371
chapters/chapter-5/main.go
Normal file
@@ -0,0 +1,371 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 5.1 Goroutine 基础
|
||||
func say(s string) {
|
||||
for i := 0; i < 3; i++ {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
fmt.Println(s)
|
||||
}
|
||||
}
|
||||
|
||||
func goroutineBasic() {
|
||||
fmt.Println("=== Goroutine 基础 ===")
|
||||
go say("world")
|
||||
say("hello")
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 5.2 GMP 模型演示
|
||||
func gmpDemo() {
|
||||
fmt.Println("\n=== GMP 模型 ===")
|
||||
fmt.Printf("GOMAXPROCS: %d\n", runtime.GOMAXPROCS(0))
|
||||
fmt.Printf("NumCPU: %d\n", runtime.NumCPU())
|
||||
fmt.Printf("NumGoroutine: %d\n", runtime.NumGoroutine())
|
||||
}
|
||||
|
||||
// 5.3 Channel 基础
|
||||
func channelBasic() {
|
||||
fmt.Println("\n=== Channel 基础 ===")
|
||||
|
||||
// 无缓冲 Channel
|
||||
ch := make(chan int)
|
||||
go func() {
|
||||
ch <- 42
|
||||
}()
|
||||
val := <-ch
|
||||
fmt.Println("无缓冲 Channel:", val)
|
||||
|
||||
// 有缓冲 Channel
|
||||
bufferedCh := make(chan int, 2)
|
||||
bufferedCh <- 1
|
||||
bufferedCh <- 2
|
||||
fmt.Println("有缓冲 Channel:", <-bufferedCh, <-bufferedCh)
|
||||
}
|
||||
|
||||
// 5.3 Channel 关闭与遍历
|
||||
func channelClose() {
|
||||
fmt.Println("\n=== Channel 关闭与遍历 ===")
|
||||
ch := make(chan int)
|
||||
|
||||
go func() {
|
||||
for i := 0; i < 5; i++ {
|
||||
ch <- i
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
|
||||
for val := range ch {
|
||||
fmt.Print(val, " ")
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
// 检查关闭
|
||||
if _, ok := <-ch; !ok {
|
||||
fmt.Println("Channel 已关闭")
|
||||
}
|
||||
}
|
||||
|
||||
// 5.3 工作池
|
||||
func worker(id int, jobs <-chan int, results chan<- int) {
|
||||
for j := range jobs {
|
||||
fmt.Printf("Worker %d 处理 %d\n", id, j)
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
results <- j * 2
|
||||
}
|
||||
}
|
||||
|
||||
func workerPoolDemo() {
|
||||
fmt.Println("\n=== 工作池 ===")
|
||||
jobs := make(chan int, 10)
|
||||
results := make(chan int, 10)
|
||||
|
||||
for w := 1; w <= 3; w++ {
|
||||
go worker(w, jobs, results)
|
||||
}
|
||||
|
||||
for j := 1; j <= 5; j++ {
|
||||
jobs <- j
|
||||
}
|
||||
close(jobs)
|
||||
|
||||
for r := 1; r <= 5; r++ {
|
||||
<-results
|
||||
}
|
||||
fmt.Println("工作池完成")
|
||||
}
|
||||
|
||||
// 5.3 Select
|
||||
func selectDemo() {
|
||||
fmt.Println("\n=== Select ===")
|
||||
ch1 := make(chan string)
|
||||
ch2 := make(chan string)
|
||||
|
||||
go func() {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
ch1 <- "来自 ch1"
|
||||
}()
|
||||
|
||||
go func() {
|
||||
time.Sleep(300 * time.Millisecond)
|
||||
ch2 <- "来自 ch2"
|
||||
}()
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case msg1 := <-ch1:
|
||||
fmt.Println(msg1)
|
||||
case msg2 := <-ch2:
|
||||
fmt.Println(msg2)
|
||||
case <-time.After(1 * time.Second):
|
||||
fmt.Println("超时")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5.4 WaitGroup
|
||||
func waitGroupDemo() {
|
||||
fmt.Println("\n=== WaitGroup ===")
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func(id int) {
|
||||
defer wg.Done()
|
||||
fmt.Printf("Worker %d 开始\n", id)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
fmt.Printf("Worker %d 完成\n", id)
|
||||
}(i)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Println("所有任务完成")
|
||||
}
|
||||
|
||||
// 5.4 Mutex
|
||||
func mutexDemo() {
|
||||
fmt.Println("\n=== Mutex ===")
|
||||
var mu sync.Mutex
|
||||
var count int
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
go func() {
|
||||
mu.Lock()
|
||||
count++
|
||||
mu.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Count:", count)
|
||||
}
|
||||
|
||||
// 5.4 RWMutex
|
||||
func rwMutexDemo() {
|
||||
fmt.Println("\n=== RWMutex ===")
|
||||
var mu sync.RWMutex
|
||||
var data = make(map[string]int)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
// 读操作
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
mu.RLock()
|
||||
_ = data["key"]
|
||||
mu.RUnlock()
|
||||
}()
|
||||
}
|
||||
|
||||
// 写操作
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
mu.Lock()
|
||||
data["key"]++
|
||||
mu.Unlock()
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
fmt.Println("Data:", data)
|
||||
}
|
||||
|
||||
// 5.4 Once
|
||||
func onceDemo() {
|
||||
fmt.Println("\n=== Once ===")
|
||||
var once sync.Once
|
||||
setup := func() {
|
||||
fmt.Println("初始化一次")
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
go func() {
|
||||
once.Do(setup)
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// 5.5 Atomic
|
||||
func atomicDemo() {
|
||||
fmt.Println("\n=== Atomic ===")
|
||||
var count int64
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
go func() {
|
||||
atomic.AddInt64(&count, 1)
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Atomic Count:", atomic.LoadInt64(&count))
|
||||
}
|
||||
|
||||
// 5.6 Context
|
||||
func contextDemo() {
|
||||
fmt.Println("\n=== Context ===")
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
|
||||
go func() {
|
||||
select {
|
||||
case <-time.After(1 * time.Second):
|
||||
fmt.Println("任务完成")
|
||||
case <-ctx.Done():
|
||||
fmt.Println("任务取消:", ctx.Err())
|
||||
}
|
||||
}()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
// 5.8 管道
|
||||
func pipelineDemo() {
|
||||
fmt.Println("\n=== 管道 ===")
|
||||
|
||||
// 生成
|
||||
gen := func(nums ...int) <-chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
for _, n := range nums {
|
||||
out <- n
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
// 平方
|
||||
sq := func(in <-chan int) <-chan int {
|
||||
out := make(chan int)
|
||||
go func() {
|
||||
for n := range in {
|
||||
out <- n * n
|
||||
}
|
||||
close(out)
|
||||
}()
|
||||
return out
|
||||
}
|
||||
|
||||
c := gen(1, 2, 3)
|
||||
c = sq(c)
|
||||
|
||||
for n := range c {
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
|
||||
// 5.9 并发爬虫
|
||||
type Crawler struct {
|
||||
visited map[string]bool
|
||||
mu sync.Mutex
|
||||
wg sync.WaitGroup
|
||||
limit chan struct{}
|
||||
}
|
||||
|
||||
func (c *Crawler) crawl(url string, depth int) {
|
||||
if depth <= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
c.limit <- struct{}{}
|
||||
defer func() { <-c.limit }()
|
||||
defer c.wg.Done()
|
||||
|
||||
c.mu.Lock()
|
||||
if c.visited[url] {
|
||||
c.mu.Unlock()
|
||||
return
|
||||
}
|
||||
c.visited[url] = true
|
||||
c.mu.Unlock()
|
||||
|
||||
fmt.Println("抓取:", url)
|
||||
|
||||
// 模拟子链接
|
||||
links := []string{url + "/1", url + "/2"}
|
||||
for _, link := range links {
|
||||
c.wg.Add(1)
|
||||
go c.crawl(link, depth-1)
|
||||
}
|
||||
}
|
||||
|
||||
func crawlerDemo() {
|
||||
fmt.Println("\n=== 并发爬虫 ===")
|
||||
crawler := &Crawler{
|
||||
visited: make(map[string]bool),
|
||||
limit: make(chan struct{}, 5),
|
||||
}
|
||||
|
||||
crawler.wg.Add(1)
|
||||
go crawler.crawl("http://example.com", 2)
|
||||
|
||||
crawler.wg.Wait()
|
||||
fmt.Println("爬虫完成")
|
||||
}
|
||||
|
||||
// 5.10 竞态检测示例(需配合 -race 运行)
|
||||
func raceDemo() {
|
||||
fmt.Println("\n=== 竞态检测示例 ===")
|
||||
var count int
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
go func() {
|
||||
count++ // 竞态!
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Count:", count)
|
||||
}
|
||||
|
||||
func main() {
|
||||
goroutineBasic()
|
||||
gmpDemo()
|
||||
channelBasic()
|
||||
channelClose()
|
||||
workerPoolDemo()
|
||||
selectDemo()
|
||||
waitGroupDemo()
|
||||
mutexDemo()
|
||||
rwMutexDemo()
|
||||
onceDemo()
|
||||
atomicDemo()
|
||||
contextDemo()
|
||||
pipelineDemo()
|
||||
crawlerDemo()
|
||||
raceDemo()
|
||||
}
|
||||
Reference in New Issue
Block a user