Golang,也被称为Go语言,由于其简洁、高效和并发性能出色,在许多需要高并发处理的场景中备受青睐。掌握Golang的高并发编程,对于提升应用程序的性能和响应速度至关重要。本文将为您提供10个实战示例代码解析,并附上学习指南,帮助您深入理解Golang的高并发编程。
实战示例一:使用goroutine处理并发下载
在Golang中,goroutine是并发编程的核心。以下是一个使用goroutine进行并发下载的示例:
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func download(url, dest string) error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
out, err := os.Create(dest)
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, resp.Body)
return err
}
func main() {
urls := []string{
"https://example.com/file1.zip",
"https://example.com/file2.zip",
}
for _, url := range urls {
go download(url, fmt.Sprintf("downloaded_%s", url))
}
}
在这个示例中,我们定义了一个download函数,它使用http.Get发送HTTP请求,并将响应体写入到文件中。在main函数中,我们为每个下载任务启动一个goroutine。
实战示例二:使用channel同步goroutine
在某些情况下,我们需要同步多个goroutine的执行。以下是一个使用channel同步goroutine的示例:
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup, done chan bool) {
defer wg.Done()
fmt.Printf("Worker %d is working...\n", id)
// 模拟工作
for i := 0; i < 5; i++ {
fmt.Printf("Worker %d is doing task %d\n", id, i)
}
// 通知工作完成
done <- true
}
func main() {
var wg sync.WaitGroup
done := make(chan bool)
for i := 0; i < 3; i++ {
wg.Add(1)
go worker(i, &wg, done)
}
wg.Wait()
close(done)
fmt.Println("All workers are done!")
for v := range done {
fmt.Println("Worker done:", v)
}
}
在这个示例中,我们定义了一个worker函数,它将模拟一些工作,并在完成后将一个布尔值发送到done通道。在main函数中,我们启动了3个goroutine,并在所有goroutine完成后关闭done通道。
实战示例三:使用context处理超时和取消
在并发编程中,处理超时和取消请求是非常重要的。以下是一个使用context处理超时和取消的示例:
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, id int) {
select {
case <-time.After(2 * time.Second):
fmt.Printf("Worker %d is working...\n", id)
case <-ctx.Done():
fmt.Printf("Worker %d is cancelled\n", id)
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
for i := 0; i < 5; i++ {
go worker(ctx, i)
}
time.Sleep(4 * time.Second)
}
在这个示例中,我们定义了一个worker函数,它将在2秒后完成工作。在main函数中,我们使用context.WithTimeout创建了一个带有超时的context,并在4秒后关闭了goroutine。
实战示例四:使用sync.Mutex保护共享资源
在并发编程中,共享资源可能会导致竞态条件。以下是一个使用sync.Mutex保护共享资源的示例:
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex
count int
)
func increment() {
mu.Lock()
defer mu.Unlock()
count++
fmt.Printf("Count: %d\n", count)
}
func main() {
for i := 0; i < 10; i++ {
go increment()
}
}
在这个示例中,我们定义了一个increment函数,它将增加count变量的值。我们使用sync.Mutex来保护count变量,以避免竞态条件。
实战示例五:使用sync.RWMutex保护共享资源
在某些情况下,我们需要允许多个读操作同时进行,但只允许一个写操作。以下是一个使用sync.RWMutex保护共享资源的示例:
package main
import (
"fmt"
"sync"
"time"
)
var (
mu sync.RWMutex
count int
)
func read() {
mu.RLock()
defer mu.RUnlock()
fmt.Printf("Count: %d\n", count)
}
func write() {
mu.Lock()
defer mu.Unlock()
count++
fmt.Printf("Count: %d\n", count)
}
func main() {
for i := 0; i < 10; i++ {
go read()
go write()
}
time.Sleep(2 * time.Second)
}
在这个示例中,我们定义了read和write函数,分别用于读取和写入共享资源。我们使用sync.RWMutex来允许多个读操作同时进行,但只允许一个写操作。
实战示例六:使用sync.WaitGroup等待goroutine完成
在某些情况下,我们需要等待多个goroutine完成才能继续执行。以下是一个使用sync.WaitGroup等待goroutine完成的示例:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d is working...\n", id)
// 模拟工作
time.Sleep(time.Duration(id) * time.Second)
fmt.Printf("Worker %d is done\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("All workers are done!")
}
在这个示例中,我们定义了一个worker函数,它将模拟一些工作,并在完成后通知sync.WaitGroup。在main函数中,我们启动了5个goroutine,并使用sync.WaitGroup等待它们完成。
实战示例七:使用channel进行生产者-消费者模式
生产者-消费者模式是并发编程中常见的一种模式。以下是一个使用channel进行生产者-消费者模式的示例:
package main
import (
"fmt"
"time"
)
func producer(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
fmt.Printf("Produced %d\n", i)
time.Sleep(time.Duration(i) * time.Second)
}
close(ch)
}
func consumer(ch chan int) {
for v := range ch {
fmt.Printf("Consumed %d\n", v)
time.Sleep(time.Duration(v) * time.Second)
}
}
func main() {
ch := make(chan int)
go producer(ch)
go consumer(ch)
}
在这个示例中,我们定义了producer和consumer函数,分别用于生产数据和消费数据。我们使用channel来实现生产者-消费者模式。
实战示例八:使用context传递取消信号
在某些情况下,我们需要传递取消信号给多个goroutine。以下是一个使用context传递取消信号的示例:
package main
import (
"context"
"fmt"
"sync"
"time"
)
func worker(ctx context.Context, id int) {
select {
case <-time.After(time.Duration(id) * time.Second):
fmt.Printf("Worker %d is working...\n", id)
case <-ctx.Done():
fmt.Printf("Worker %d is cancelled\n", id)
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for i := 0; i < 5; i++ {
go worker(ctx, i)
}
time.Sleep(2 * time.Second)
cancel()
time.Sleep(1 * time.Second)
}
在这个示例中,我们定义了一个worker函数,它将在2秒后完成工作。在main函数中,我们使用context.WithCancel创建了一个可取消的context,并在2秒后取消了goroutine。
实战示例九:使用select语句处理多个channel
在某些情况下,我们需要同时监听多个channel。以下是一个使用select语句处理多个channel的示例:
package main
import (
"fmt"
"time"
)
func sender(ch chan int) {
for i := 0; i < 10; i++ {
ch <- i
fmt.Printf("Sent %d\n", i)
time.Sleep(time.Duration(i) * time.Second)
}
close(ch)
}
func receiver(ch chan int) {
for v := range ch {
fmt.Printf("Received %d\n", v)
time.Sleep(time.Duration(v) * time.Second)
}
}
func main() {
ch := make(chan int)
go sender(ch)
go receiver(ch)
select {
case v := <-ch:
fmt.Printf("Received %d from channel\n", v)
case <-time.After(10 * time.Second):
fmt.Println("Timeout")
}
}
在这个示例中,我们定义了sender和receiver函数,分别用于发送和接收数据。在main函数中,我们使用select语句同时监听ch通道和超时信号。
实战示例十:使用sync/atomic操作原子类型
在某些情况下,我们需要对原子类型进行操作。以下是一个使用sync/atomic操作原子类型的示例:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var (
count int64
wg sync.WaitGroup
)
func increment() {
atomic.AddInt64(&count, 1)
wg.Done()
}
func main() {
for i := 0; i < 1000; i++ {
wg.Add(1)
go increment()
}
wg.Wait()
fmt.Printf("Count: %d\n", atomic.LoadInt64(&count))
}
在这个示例中,我们定义了一个increment函数,它将原子地增加count变量的值。在main函数中,我们启动了1000个goroutine,并使用sync.WaitGroup等待它们完成。
通过以上10个实战示例代码解析,相信您已经对Golang的高并发编程有了更深入的了解。在学习和实践过程中,请务必结合实际项目进行尝试,以便更好地掌握Golang的高并发编程技巧。
