在Golang中,并发编程是一个重要的特性,它使得开发者能够利用多核处理器的能力,提高程序的执行效率。然而,并发编程也带来了一系列挑战,比如竞态条件、死锁等问题。本篇文章将通过5个实用的示例代码,帮助读者深入理解Golang中的并发控制。
示例1:使用sync.Mutex保护共享资源
在并发编程中,共享资源是并发控制的关键点。以下代码展示了如何使用sync.Mutex来保护共享资源,防止竞态条件的发生。
package main
import (
"sync"
"fmt"
)
var (
counter int
mu sync.Mutex
)
func increment() {
mu.Lock()
defer mu.Unlock()
counter++
}
func main() {
for i := 0; i < 1000; i++ {
go increment()
}
fmt.Println(counter)
}
在这个示例中,increment函数通过加锁和释放锁来保护共享资源counter,确保在任意时刻只有一个goroutine可以修改它。
示例2:使用sync.WaitGroup等待多个goroutine完成
sync.WaitGroup是Golang中用于等待多个goroutine完成的工具。以下代码展示了如何使用sync.WaitGroup来同步多个goroutine。
package main
import (
"sync"
"fmt"
)
func worker(wg *sync.WaitGroup) {
defer wg.Done()
// 执行一些任务
fmt.Println("Worker done")
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(&wg)
}
wg.Wait()
}
在这个示例中,主goroutine通过调用wg.Wait()来等待所有worker goroutine完成。
示例3:使用sync.RWMutex实现读写锁
sync.RWMutex是sync.Mutex的读写版本,允许多个goroutine同时读取,但只允许一个goroutine写入。以下代码展示了如何使用sync.RWMutex。
package main
import (
"sync"
"fmt"
)
var (
counter int
mu sync.RWMutex
)
func read() {
mu.RLock()
defer mu.RUnlock()
fmt.Println(counter)
}
func write() {
mu.Lock()
defer mu.Unlock()
counter++
}
func main() {
for i := 0; i < 10; i++ {
go read()
}
go write()
fmt.Println(counter)
}
在这个示例中,read函数可以同时被多个goroutine调用,而write函数则确保在同一时间只有一个goroutine可以写入。
示例4:使用channel实现goroutine间的通信
channel是Golang中goroutine间通信的主要方式。以下代码展示了如何使用channel在goroutine间传递数据。
package main
import (
"fmt"
)
func producer(ch chan int) {
for i := 0; i < 5; i++ {
ch <- i
}
close(ch)
}
func consumer(ch chan int) {
for v := range ch {
fmt.Println(v)
}
}
func main() {
ch := make(chan int)
go producer(ch)
go consumer(ch)
}
在这个示例中,producer函数通过channel发送数据,而consumer函数则从channel接收数据。
示例5:使用context包控制goroutine的生命周期
context包是Golang中用于控制goroutine生命周期的工具。以下代码展示了如何使用context包。
package main
import (
"context"
"fmt"
"time"
)
func worker(ctx context.Context) {
select {
case <-ctx.Done():
fmt.Println("Worker cancelled")
return
default:
fmt.Println("Worker running")
time.Sleep(2 * time.Second)
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
go worker(ctx)
time.Sleep(2 * time.Second)
}
在这个示例中,worker函数在接收到context.Done()信号后退出。主goroutine通过context.WithTimeout创建了一个带有超时的context,并在超时后取消goroutine。
通过以上5个示例代码,相信读者已经对Golang并发控制有了更深入的理解。在实际开发中,合理运用并发控制工具,能够提高程序的执行效率,同时保证程序的正确性和稳定性。
