Golang 不可不知的 7 个并发概念

简介: Golang 不可不知的 7 个并发概念

并发性支持是 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类型的缓冲通道。然后使用<-操作符向通道发送两个值(12)。最后,我们使用<-操作符从通道接收值,并打印到控制台。

4. Select 语句

Go 中的 select 语句允许我们同时等待多个通道操作。这是个强大的结构,可以帮助我们编排复杂的并发系统。select 语句会阻塞直到其中一个 case 可以继续进行,此时就执行该 case。


下面是一个使用 select 语句的例子:



本例中创建了两个通道(ch1ch2)和两个向这些通道发送消息的 goroutine。然后,我们使用 select 语句等待消息到达ch1ch2。当消息到达时,将其打印到控制台。

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.Contextsync.WaitGroup作为参数的worker函数。worker函数用select语句等待来自 context 的 cancel 信号或者在 default 中执行某些工作。我们还定义了一个main函数,该函数用context.WithCancel创建上下文,并启动worker goroutine。等待 5 秒后 cancel context,它会向 worker goroutine 发送 cancel 信号,让它停止工作。在打印最终消息之前,我们用 WaitGroup 来等待 worker goroutine 结束。

结论

总的来说,并发性是 Go 中的一个重要主题,并且该语言为处理并发系统提供了一组强大的工具。无论是构建 web 服务器、分布式系统还是简单的命令行工具,了解 Go 的并发性对于构建健壮、可扩展、高效的程序都必不可少。




你好,我是俞凡,在 Motorola 做过研发,现在在 Mavenir 做技术工作,对通信、网络、后端架构、云原生、DevOps、CICD、区块链、AI 等技术始终保持着浓厚的兴趣,平时喜欢阅读、思考,相信持续学习、终身成长,欢迎一起交流学习。为了方便大家以后能第一时间看到文章,请朋友们关注公众号"DeepNoMind",并设个星标吧,如果能一键三连(转发、点赞、在看),则能给我带来更多的支持和动力,激励我持续写下去,和大家共同成长进步!

目录
相关文章
|
6月前
|
Go
浅谈Golang并发控制WaitGroup
浅谈Golang并发控制WaitGroup
56 0
|
存储 安全 编译器
Golang 语言中 map 的键值类型选择,它是并发安全的吗?
Golang 语言中 map 的键值类型选择,它是并发安全的吗?
70 0
|
2月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
86 4
|
6月前
|
Go
深度探讨 Golang 中并发发送 HTTP 请求的最佳技术
深度探讨 Golang 中并发发送 HTTP 请求的最佳技术
122 4
|
6月前
|
存储 缓存 安全
Golang深入浅出之-Go语言中的并发安全容器:sync.Map与sync.Pool
Go语言中的`sync.Map`和`sync.Pool`是并发安全的容器。`sync.Map`提供并发安全的键值对存储,适合快速读取和少写入的情况。注意不要直接遍历Map,应使用`Range`方法。`sync.Pool`是对象池,用于缓存可重用对象,减少内存分配。使用时需注意对象生命周期管理和容量控制。在多goroutine环境下,这两个容器能提高性能和稳定性,但需根据场景谨慎使用,避免不当操作导致的问题。
194 7
|
6月前
|
安全 Go 开发者
Golang深入浅出之-Go语言中的CSP模型:深入理解并发哲学
【5月更文挑战第2天】Go语言的并发编程基于CSP模型,强调通过通信共享内存。核心概念是goroutines(轻量级线程)和channels(用于goroutines间安全数据传输)。常见问题包括数据竞争、死锁和goroutine管理。避免策略包括使用同步原语、复用channel和控制并发。示例展示了如何使用channel和`sync.WaitGroup`避免死锁。理解并发原则和正确应用CSP模型是编写高效安全并发程序的关键。
167 7
|
6月前
|
安全 Go
Golang深入浅出之-Go语言中的并发安全队列:实现与应用
【5月更文挑战第3天】本文探讨了Go语言中的并发安全队列,它是构建高性能并发系统的基础。文章介绍了两种实现方法:1) 使用`sync.Mutex`保护的简单队列,通过加锁解锁确保数据一致性;2) 使用通道(Channel)实现无锁队列,天生并发安全。同时,文中列举了并发编程中常见的死锁、数据竞争和通道阻塞问题,并给出了避免这些问题的策略,如明确锁边界、使用带缓冲通道、优雅处理关闭以及利用Go标准库。
446 5
|
6月前
|
安全 Go 开发者
Golang深入浅出之-Go语言中的CSP模型:深入理解并发哲学
【5月更文挑战第1天】Go语言基于CSP理论,借助goroutines和channels实现独特的并发模型。Goroutine是轻量级线程,通过`go`关键字启动,而channels提供安全的通信机制。文章讨论了数据竞争、死锁和goroutine泄漏等问题及其避免方法,并提供了一个生产者消费者模型的代码示例。理解CSP和妥善处理并发问题对于编写高效、可靠的Go程序至关重要。
150 2
|
6月前
|
设计模式 Go 调度
Golang深入浅出之-Go语言中的并发模式:Pipeline、Worker Pool等
【5月更文挑战第1天】Go语言并发模拟能力强大,Pipeline和Worker Pool是常用设计模式。Pipeline通过多阶段处理实现高效并行,常见问题包括数据竞争和死锁,可借助通道和`select`避免。Worker Pool控制并发数,防止资源消耗,需注意任务分配不均和goroutine泄露,使用缓冲通道和`sync.WaitGroup`解决。理解和实践这些模式是提升Go并发性能的关键。
79 2
|
Go
Golang 语言怎么控制并发 goroutine?
Golang 语言怎么控制并发 goroutine?
42 0