在Golang(Go语言)中,并发编程是一种强大的特性,允许你同时执行多个任务,从而提高程序的效率。对于初学者来说,理解并发编程的概念和如何在实际项目中应用它可能有些挑战。下面,我将通过5个实用示例,帮助你轻松入门Golang并发编程。
示例1:使用goroutine打印数字序列
在Golang中,goroutine是并发执行的基本单位。以下是一个简单的例子,展示如何使用goroutine来打印一个数字序列。
package main
import (
"fmt"
"sync"
)
func printNumbers(wg *sync.WaitGroup, start, end int) {
for i := start; i <= end; i++ {
fmt.Println(i)
}
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(2)
go printNumbers(&wg, 1, 10)
go printNumbers(&wg, 11, 20)
wg.Wait()
}
在这个示例中,我们创建了两个goroutine,分别打印1到10和11到20的数字序列。通过sync.WaitGroup,我们确保主goroutine在所有子goroutine完成工作后才继续执行。
示例2:使用channel同步goroutine
channel是Golang中用于goroutine之间通信的结构。以下示例展示了如何使用channel来同步两个goroutine。
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, wg *sync.WaitGroup, done chan<- bool) {
defer wg.Done()
for j := range jobs {
fmt.Printf("Worker %d processed job %d\n", id, j)
}
done <- true
}
func main() {
const numWorkers = 3
jobs := make(chan int, 10)
done := make(chan bool, numWorkers)
var wg sync.WaitGroup
wg.Add(numWorkers)
for w := 1; w <= numWorkers; w++ {
go worker(w, jobs, &wg, done)
}
for j := 1; j <= 10; j++ {
jobs <- j
}
close(jobs)
for w := 1; w <= numWorkers; w++ {
done <- true
}
close(done)
wg.Wait()
}
在这个例子中,我们创建了三个worker goroutines,它们从jobs channel中获取任务并处理。当所有任务完成后,我们关闭jobs和done channels,并等待所有worker完成。
示例3:使用select语句处理多个channel
select语句允许你等待多个channel操作完成。以下示例展示了如何使用select来处理多个channel。
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("Received", msg1)
case msg2 := <-c2:
fmt.Println("Received", msg2)
}
}
}
在这个例子中,我们有两个goroutines,每个goroutine向一个channel发送消息。select语句等待从任一channel接收消息,并在接收到消息时打印出来。
示例4:使用sync.Mutex保护共享资源
当多个goroutine访问共享资源时,需要使用同步机制来避免竞态条件。以下示例展示了如何使用sync.Mutex来保护共享资源。
package main
import (
"fmt"
"sync"
)
type Counter struct {
mu sync.Mutex
n int
}
func (c *Counter) Increment() {
c.mu.Lock()
c.n++
c.mu.Unlock()
}
func (c *Counter) Value() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.n
}
func main() {
c := Counter{}
for i := 0; i < 1000; i++ {
go c.Increment()
}
fmt.Println("Counter value:", c.Value())
}
在这个例子中,我们创建了一个Counter结构,它有一个互斥锁和一个整数。Increment方法使用互斥锁来确保同时只有一个goroutine可以修改计数器的值。
示例5:使用context包取消goroutine
context包提供了取消goroutine的机制,这在处理长时间运行的任务时非常有用。以下示例展示了如何使用context来取消goroutine。
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context, id int) {
for {
select {
case <-ctx.Done():
fmt.Printf("Worker %d cancelled\n", id)
return
default:
fmt.Printf("Worker %d working\n", id)
time.Sleep(time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for i := 1; i <= 5; i++ {
go worker(ctx, i)
}
time.Sleep(time.Second * 3)
cancel()
}
在这个例子中,我们创建了一个可以取消的context。每个worker goroutine都会检查context是否被取消,如果被取消,则退出循环并停止执行。通过调用cancel(),我们可以停止所有worker goroutine。
