今天想结合总结下go语言的并发相关的内容。Go语言就像一位擅长多任务处理卷王自带的处理多任务的功能。
并发与并行的区别
我们先来明确两个概念,老师在课程中提到的是并发是指程序中多个任务交替执行的能力,而并行是指这些任务同时执行的能力。在单核处理器上,通过快速切换任务来实现并发;而在多核处理器上,可以实现真正的并行执行。
Go语言亮点在于并发,通过Goroutine和Channel的组合,可以轻松实现高效的并发编程。
Goroutine:Go语言的轻量级线程
在Go语言中,Goroutine是一种轻量级的执行单元,类似于线程,但比线程更加高效。Goroutine的启动成本很低,可以在一个程序中创建成千上万个Goroutine,而不会导致资源耗尽。这种低启动成本是由Go的运行时系统负责管理Goroutine的方式所实现的。Go运行时会自动复用线程,将多个Goroutine映射到少量的线程上执行。这种方式不仅节省了内存和系统资源,还允许程序在高并发情况下表现出色。
func main() {
go func1() {
PASS
}
go func2() {
PASS
}
}
我们使用了go关键字来启动一个Goroutine当main函数返回时,所有的Goroutine都会被中止。这里的应用就是课程后续简单翻译字典作业里面要求使用并发提高两个字典的查询速度,这里只需要用两个查询函数替换func1和func2就好。
Channel:Goroutine之间的通信
创建Channel
在Go中可以通过make函数来创建一个Channel。例如,创建一个可以传递整数的Channel:
ch := make(chan int)
发送数据到Channel
要发送数据到Channel,使用<-操作符将数据发送到Channel中。这样,Goroutine会等待,直到有其他Goroutine从Channel接收这个数据。
ch <- 42 // 发送数据到Channel
从Channel接收数据
从Channel接收数据也是使用<-操作符,将数据从Channel中取出。如果Channel中没有数据,接收操作会阻塞,直到有数据可用。
value := <-ch // 从Channel接收数据
关闭Channel
可以使用close函数来关闭一个Channel。关闭后的Channel不能再发送数据,但仍然可以从中接收数据。
close(ch)
无缓冲和有缓冲的Channel
Go语言中的Channel可以分为无缓冲和有缓冲两种类型。
- 无缓冲的Channel:在发送数据时,发送者会阻塞直到有接收者接收这个数据。这样可以确保数据在Goroutine之间同步传递。
ch := make(chan int)
- 有缓冲的Channel:有缓冲的Channel允许在Channel中存储一定数量的数据,当Channel已满时,发送者会阻塞。只有当Channel被填满后,发送者才会被阻塞。
ch := make(chan int, 10) // 创建一个容量为10的有缓冲Channel
Mutex与互斥锁:保护共享资源
在多个Goroutine访问共享资源时,可能会引发数据竞争和不确定的行为。**互斥锁(Mutex)**是一种机制,可以确保在任意时刻只有一个Goroutine能够访问共享资源。
import (
"sync"
)
var count int
var mutex sync.Mutex
func increment() {
mutex.Lock()
defer mutex.Unlock()
count++
}
func main() {
for i := 0; i < 1000; i++ {
go increment()
}
// 等待所有Goroutine执行完毕
// ...
fmt.Println("Final count:", count)
}
在上面的例子中,声明了一个共享的整数变量count,它将被多个Goroutine访问和修改。同时,我创建了一个互斥锁mutex,它将用于保护对count的访问。- 名为increment的函数,它会在每个Goroutine中执行。这个函数使用互斥锁确保每次只有一个Goroutine可以进入临界区(即修改count的代码块)。mutex.Lock()会锁住互斥锁,阻止其他Goroutine进入,直到当前Goroutine解锁为止。在这里,我们使用defer语句确保在函数结束时自动解锁互斥锁,以避免锁被长时间占用。
defer wg.Done()用于标记一个Goroutine已经完成,这将在sync.WaitGroup中减少等待的计数。- 在临界区内,我们将
count增加1,这是一个共享资源。
以上是关于并发的基本的概念和总结,对于后续的具体项目中的应用在大项目中使用~