并发性支持是Golang最重要的原生特性之一,本文介绍了Golang中和并发性相关的7个概念。原文: Golang: 7 must-know concurrency related concepts
并发是Go编程语言的基本特性,意味着程序可以同时执行多个任务。Golang的并发独特而强大,其内置的轻量级协程(goroutine)和通道(channel)支持创建可伸缩、安全、高性能的高并发系统。
本文将探索Go中和并发性有关的七个有趣事实,并提供示例。
1. 协程(Goroutines)
Goroutine是Go编程语言的特性之一,这是轻量级线程,与同一地址空间中的其他goroutine并发运行。它的创建成本非常低,Go运行时可以同时处理数千个goroutine。Goroutine使编写高并发程序变得容易,这些程序可以根据需要伸缩。
下面是一个创建goroutine的例子:
在本例中,定义了printMessage
函数,该函数接受一个消息字符串以及打印消息的次数,其中sleep语句用来模拟在每次消息打印之间完成的一些额外工作。
在main
函数中,调用go printMessage("Hello", 5)
和go printMessage("world", 5)
来启动两个goroutine。这创建了两个与主线程并发运行的独立执行线程。time.Sleep(1 * time.Second)
语句用于将主线程暂停一秒钟,这给了两个goroutine足够的时间来执行和打印消息。
2. 通道(Channels)
Channel是Go的另一个基本特性,支持在程序之间进行通信和同步。Channel是一种有类型管道,可以使用<-
操作符发送和接收。Channel确保了并发进程之间安全有效的通信。
下面是一个使用channel的例子:
在本例中,make
函数创建了一个string
类型的channel。然后,我们创建一个使用<-
操作符向channel发送消息"Hello from channel!"的goroutine。最后,我们使用<-
操作符从channel接收消息并将其打印到控制台。
3. 缓冲通道(Buffered Channels)
缓冲通道是在读取之前可以保存一定数量的值的通道,对于管理并发系统中的突发流量非常有用。使用make
函数可以创建缓冲通道,通过第二个参数指定缓冲区大小。
下面是一个使用缓冲通道的例子:
在本例中,我们创建了一个缓冲区大小为2的int
类型的缓冲通道。然后使用<-
操作符向通道发送两个值(1
和2
)。最后,我们使用<-
操作符从通道接收值,并打印到控制台。
4. Select语句
Go中的select语句允许我们同时等待多个通道操作。这是个强大的结构,可以帮助我们编排复杂的并发系统。select语句会阻塞直到其中一个case可以继续进行,此时就执行该case。
下面是一个使用select语句的例子:
本例中创建了两个通道(ch1
和ch2
)和两个向这些通道发送消息的goroutine。然后,我们使用select语句等待消息到达ch1
或ch2
。当消息到达时,将其打印到控制台。
5. Mutex
Go中的互斥锁(sync.Mutex
)提供了一种简单有效的方法来保护共享资源免受并发访问。mutex是一种互斥锁,一次只允许一个程序访问资源,任何其他试图在资源被锁定时访问该资源的goroutine都将被阻塞,直到锁被释放。
下面是一个使用互斥锁的例子:
在这个例子中,我们定义了一个Counter
类型,包含count
字段和一个sync.Mutex
。在Counter
类型上定义了两个方法: Increment()
和Count()
。这两种方法都使用互斥锁来确保一次只有一个goroutine可以访问count
字段。最后,我们创建1000个增加count
字段的goroutine,在打印最终计数之前等待它们全部完成。
6. WaitGroup
Go中的sync.WaitGroup
类型提供了一种同步多个goroutine的简单方法。WaitGroup在继续之前等待一组goroutine完成,是协调多个goroutine执行的有力工具,可以帮助我们确保在进入程序的下一步之前,所有goroutine都已完成。
下面是一个使用WaitGroup的例子:
在本例中,我们创建了一个WaitGroup和10个goroutine。每个goroutine休眠数秒,然后向控制台打印一条消息。我们使用WaitGroup来确保在打印最终消息之前所有的goroutine都已经完成。
7. Context
Go中的context
包提供了一种跨API边界和进程之间携带截止日期、取消信号和其他请求作用域值的方法,是在并发系统中管理资源的强大工具,可以帮助我们避免常见问题(如goroutine泄漏)。
下面是一个使用context的例子:
func worker(ctx Context.context, wg *sync.WaitGroup) {
defer wg.Done()
for {
select {
case <- ctx.Done():
fmt.Println("Worker received cancel signal")
return
default:
fmt.Println("Worker is working...")
time.Sleep(1 * time.Second)
}
}
}
func main() {
ctx, cancel := context.WithCancel()
var wg sync.WaitGroup
wg.Add(1)
go worker(ctx, &wg)
time.Sleep(5 * time.Second)
cancel()
wg.Wait()
fmt.Println("All workers stopped")
}
在这个例子中,我们定义了一个接受context.Context
和sync.WaitGroup
作为参数的worker
函数。worker
函数用select
语句等待来自context的cancel信号或者在default中执行某些工作。我们还定义了一个main
函数,该函数用context.WithCancel
创建上下文,并启动worker
goroutine。等待5秒后cancel context,它会向worker goroutine发送cancel信号,让它停止工作。在打印最终消息之前,我们用WaitGroup来等待worker goroutine结束。
结论
总的来说,并发性是Go中的一个重要主题,并且该语言为处理并发系统提供了一组强大的工具。无论是构建web服务器、分布式系统还是简单的命令行工具,了解Go的并发性对于构建健壮、可扩展、高效的程序都必不可少。
- END -你好,我是俞凡,在Motorola做过研发,现在在Mavenir做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!
本文由 mdnice 多平台发布