第6章:Go语言并发编程
6.1 并发基础概念
6.1.1 并发vs并行
go
复制代码
// 并发示例:模拟多任务处理
func main() {
// 并发执行多个任务
go task1()
go task2()
// 主goroutine继续执行
time.Sleep(time.Second)
}
func task1() {
fmt.Println("任务1执行")
}
func task2() {
fmt.Println("任务2执行")
}
6.1.2 Goroutine
go
复制代码
// Goroutine基本使用
func printNumbers() {
for i := 1; i <= 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Printf("数字:%d\n", i)
}
}
func printLetters() {
for i := 'a'; i <= 'e'; i++ {
time.Sleep(150 * time.Millisecond)
fmt.Printf("字母:%c\n", i)
}
}
func main() {
go printNumbers()
go printLetters()
// 等待goroutine执行
time.Sleep(time.Second)
}
6.2 通道(Channel)
6.2.1 基本通道操作
go
复制代码
func main() {
// 创建无缓冲通道
ch := make(chan int)
// 发送和接收
go func() {
ch <- 42 // 发送
}()
value := <-ch // 接收
fmt.Println(value)
}
// 带缓冲的通道
func bufferedChannelDemo() {
ch := make(chan string, 3)
ch <- "Hello"
ch <- "World"
ch <- "Go"
fmt.Println(<-ch) // 依次输出
}
6.2.2 通道方向
go
复制代码
// 只读通道
func receiver(ch <-chan int) {
for value := range ch {
fmt.Println("接收:", value)
}
}
// 只写通道
func sender(ch chan<- int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func main() {
ch := make(chan int)
go sender(ch)
receiver(ch)
}
6.3 并发模式
6.3.1 生产者-消费者模式
go
复制代码
func producer(ch chan<- int) {
for i := 0; i < 10; i++ {
ch <- i
time.Sleep(100 * time.Millisecond)
}
close(ch)
}
func consumer(ch <-chan int, done chan<- bool) {
for value := range ch {
fmt.Println("消费:", value)
}
done <- true
}
func main() {
ch := make(chan int)
done := make(chan bool)
go producer(ch)
go consumer(ch, done)
<-done // 等待消费者完成
}
6.3.2 select多路复用
go
复制代码
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(time.Second)
ch1 <- "通道1"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "通道2"
}()
select {
case msg1 := <-ch1:
fmt.Println(msg1)
case msg2 := <-ch2:
fmt.Println(msg2)
case <-time.After(3 * time.Second):
fmt.Println("超时")
}
}
6.4 同步原语
6.4.1 互斥锁
go
复制代码
type SafeCounter struct {
mu sync.Mutex
counter int
}
func (c *SafeCounter) Increment() {
c.mu.Lock()
defer c.mu.Unlock()
c.counter++
}
func (c *SafeCounter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.counter
}
func main() {
counter := &SafeCounter{}
for i := 0; i < 1000; i++ {
go counter.Increment()
}
time.Sleep(time.Second)
fmt.Println(counter.Value())
}
6.4.2 读写锁
go
复制代码
type Cache struct {
mu sync.RWMutex
data map[string]int
}
func (c *Cache) Read(key string) (int, bool) {
c.mu.RLock()
defer c.mu.RUnlock()
value, ok := c.data[key]
return value, ok
}
func (c *Cache) Write(key string, value int) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = value
}
6.4.3 原子操作
go
复制代码
var counter int64
func incrementCounter() {
atomic.AddInt64(&counter, 1)
}
func getCounter() int64 {
return atomic.LoadInt64(&counter)
}
6.5 并发实践
6.5.1 并行爬虫
go
复制代码
func fetchURL(url string, ch chan<- string) {
resp, err := http.Get(url)
if err != nil {
ch <- fmt.Sprintf("错误: %v", err)
return
}
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
ch <- fmt.Sprintf("URL: %s, 长度: %d", url, len(body))
}
func main() {
urls := []string{
"https://www.example.com",
"https://www.google.com",
"https://www.github.com",
}
ch := make(chan string, len(urls))
for _, url := range urls {
go fetchURL(url, ch)
}
for i := 0; i < len(urls); i++ {
fmt.Println(<-ch)
}
}
6.5.2 并发限流
go
复制代码
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
fmt.Printf("Worker %d 处理任务 %d\n", id, job)
time.Sleep(time.Second)
results <- job * 2
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// 创建固定数量的工作goroutine
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// 发送任务
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// 收集结果
for a := 1; a <= 5; a++ {
fmt.Println(<-results)
}
}
实践项目:简单的并发聊天服务器
go
复制代码
type Client struct {
conn net.Conn
send chan string
}
type ChatServer struct {
clients map[*Client]bool
broadcast chan string
register chan *Client
unregister chan *Client
}
func (server *ChatServer) run() {
for {
select {
case client := <-server.register:
server.clients[client] = true
case client := <-server.unregister:
if _, ok := server.clients[client]; ok {
delete(server.clients, client)
close(client.send)
}
case message := <-server.broadcast:
for client := range server.clients {
select {
case client.send <- message:
default:
close(client.send)
delete(server.clients, client)
}
}
}
}
}