协程:轻量级线程
在 Go 语言中,协程(goroutine)是轻量级的线程。它们允许开发者创建大量的并发任务,而不会像传统线程那样消耗过多的系统资源。
启动一个协程
使用 go 关键字来启动一个新的协程。下面是一个简单的例子:
// www.javascriptcn.com code example
package main
import (
"fmt"
"time"
)
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
func main() {
go say("world")
say("hello")
}在这个例子中,say("hello") 是在主协程中执行的,而 go say("world") 是在新的协程中执行的。注意,由于没有同步机制,主协程可能在 say("world") 完成之前就结束了。
使用通道进行通信和同步
通道(channel)是 Go 语言中用于协程间通信和同步的重要机制。通过通道,协程可以安全地交换数据,而无需担心竞态条件。
创建通道
使用 make 函数创建通道,指定通道的数据类型:
ch := make(chan int)
发送和接收数据
发送数据到通道使用 <- 操作符:
ch <- x // 将x发送到通道ch
从通道接收数据也使用 <- 操作符:
x := <-ch // 接收ch中的值并赋给x
无缓冲通道
无缓冲通道在接收方准备好接收之前,发送操作会阻塞:
ch := make(chan int)
go func() {
ch <- 42 // 阻塞直到有协程接收
}()
fmt.Println(<-ch) // 阻塞直到有协程发送带缓冲通道
带缓冲通道允许在没有接收者的情况下发送一定数量的值:
ch := make(chan int, 2) // 创建一个缓冲区大小为2的通道 ch <- 42 // 不会阻塞,因为缓冲区还有空间 ch <- 43 // 不会阻塞,因为缓冲区还有空间 fmt.Println(<-ch) // 阻塞,直到有协程发送数据
选择结构:多路复用
select 语句允许协程等待多个通信操作。它类似于 switch 语句,但用于处理通道操作。
// www.javascriptcn.com code example
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
ch1 <- "one"
}()
go func() {
ch2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("received", msg1)
case msg2 := <-ch2:
fmt.Println("received", msg2)
}
}互斥锁:防止竞态条件
尽管 Go 的通道提供了强大的并发控制,但在某些情况下,你可能需要使用互斥锁(mutex)来保护共享资源。
使用互斥锁
// www.javascriptcn.com code example
package main
import (
"fmt"
"sync"
"time"
)
var counter int
var mutex sync.Mutex
func increment(wg *sync.WaitGroup) {
mutex.Lock()
defer mutex.Unlock()
counter++
fmt.Println(counter)
wg.Done()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go increment(&wg)
}
wg.Wait()
fmt.Println("Final counter value:", counter)
}在这个例子中,我们使用 sync.Mutex 来保护对 counter 变量的访问,确保在同一时间只有一个协程可以修改这个变量。
通过上述示例,你可以看到 Go 语言提供的强大并发工具,使得并发编程变得简单且高效。希望这些内容能够帮助你在 Go 语言的并发编程中更进一步!