首页 > 编程语言 >go语言并发,释放程序潜能的魔力

go语言并发,释放程序潜能的魔力

时间:2023-11-06 13:45:14浏览次数:40  
标签:潜能 魔力 fmt goroutine 并发 ch go Go channel

Go语言并发:释放程序潜能的魔力

原创 Go先锋 Go先锋 2023-11-06 08:02 发表于广东 收录于合集#Go语言并发1个

Go 先锋

读完需要

9分钟

速读仅需 3 分钟

   

概述

在编程领域,处理多任务和并发操作是必不可少的。

Go 语言以其简洁而强大的并发机制而闻名。本文将简单探讨 Go 语言中的并发。

从基本概念到并发的优势,详细解释并发的原理,并通过通俗易懂的示例代码领略 Go 语言并发编程的魅力。

主要内容包括

  • 并发与并行的区别

  • Goroutine:Go 语言轻量级线程的魅力

  • Channel:实现 Goroutine 间的通信

  • Select 语句:处理多 Channel 的选择

  • 并发的优势

  • 并发中的常见问题与解决方案

  • 并发的最佳实践

 

   

1. 并发与并行的区别

在开始之前,需要明确“并发”和“并行”的区别。并发是指一个程序拥有多个独立的执行路径,而并行则是指这些执行路径同时执行。

Go 语言通过 goroutine 和 channel 实现了并发,让程序可以高效地同时执行多个任务。

 

   

2. Goroutine:Go 语言轻量级线程的魅力

2.1 Goroutine 的创建与启动

package main
import ( "fmt" "time")
func main() { for i := 1; i <= 3; i++ { go printNumber(i) }
// 等待goroutine执行完成 time.Sleep(time.Second)}
func printNumber(num int) { fmt.Println("Number:", num)}

在上面例子中,创建了三个 goroutine,它们会并发执行 printNumber 函数。

通过 go 关键字,启动了新的 goroutine,实现了并发执行的效果。

2.2 Goroutine 的同步与等待

package main
import ( "fmt" "sync" "time")
func main() { var wg sync.WaitGroup
for i := 1; i <= 3; i++ { wg.Add(1) go printNumber(i, &wg) }
// 等待所有goroutine执行完成 wg.Wait()}
func printNumber(num int, wg *sync.WaitGroup) {
// 减少WaitGroup的计数 defer wg.Done()
time.Sleep(time.Second) fmt.Println("Number:", num)}

在上面例子中,使用 sync.WaitGroup 来等待所有的 goroutine 执行完成。

每个 goroutine 执行完成后,调用 Done() 方法来减少 WaitGroup 的计数,最终实现了等待所有 goroutine 的效果。

 

   

3. Channel:实现 Goroutine 间的通信

3.1 无缓冲 Channel

package main
import ( "fmt")
func main() { ch := make(chan int)
go sendData(ch) go receiveData(ch)
// 等待一段时间,确保goroutine有足够时间执行 fmt.Scanln()}
func sendData(ch chan int) { ch <- 1 ch <- 2 ch <- 3 close(ch)}
func receiveData(ch chan int) { for { num, ok := <-ch if !ok { fmt.Println("Channel已关闭") return } fmt.Println("Received:", num) }}

在上面示例中,创建了一个无缓冲的 channel,通过它实现了 goroutine 之间的同步通信。

sendData 函数向 channel 发送数据,receiveData 函数从 channel 接收数据,通过 close(ch) 关闭 channel,通知接收方数据发送完成。

3.2 有缓冲 Channel

package main
import ( "fmt")
func main() { // 创建有缓冲的channel,缓冲大小为2 ch := make(chan int, 2)
go sendData(ch)
// 等待一段时间,确保goroutine有足够时间执行 fmt.Scanln()}
func sendData(ch chan int) { ch <- 1 fmt.Println("Sent: 1") ch <- 2 fmt.Println("Sent: 2") ch <- 3 // 此行代码会引发panic,因为channel已满}

上面例子中,创建了一个缓冲大小为 2 的 channel,通过它实现了 goroutine 之间的异步通信。

sendData 函数向 channel 发送数据,由于 channel 的缓冲大小为 2,可以发送两个数据。

当尝试发送第三个数据时,由于 channel 已满,会引发异常。

 

   

4. Select 语句:处理多 Channel 的选择

package main
import ( "fmt" "time")
func main() { ch1 := make(chan string) ch2 := make(chan string)
go func() { time.Sleep(2 * time.Second) ch1 <- "Data from Channel 1" }()
go func() { time.Sleep(1 * time.Second) ch2 <- "Data from Channel 2" }()
// 使用select语句
select { case data1 := <-ch1: fmt.Println(data1) case data2 := <-ch2: fmt.Println(data2) case <-time.After(3 * time.Second): fmt.Println("Timeout: No data received in 3 seconds.") }}

在这个例子中,创建了两个 goroutine 分别向 ch1 和 ch2 发送数据。

通过 select 语句,可以等待多个 channel 的数据,并选择第一个准备好的 channel 进行处理。

time.After(3 * time.Second) 表示等待 3 秒,如果在这个时间内没有任何 channel 的数据准备好,就会执行 Timeout 分支。

 

   

5. 并发的优势

5.1 提高程序性能

并发允许程序同时处理多个任务,而不是依次执行。这样可以充分利用 CPU 资源,提高程序的运行效率。

在多核心处理器上,并发更是能够实现真正的并行执行,极大地提高了程序的性能。

5.2 简化代码逻辑

使用 goroutine 和 channel,可以避免复杂的回调和锁机制,使得程序的逻辑更加清晰和简单。

并发模型让代码更容易理解和维护,减少了许多传统多线程编程中可能遇到的问题。

5.3 高效利用多核心处理器

在传统的多线程编程中,由于线程的创建和销毁需要时间,而且线程间的切换也会带来性能开销。

而 goroutine 是由 Go 语言的运行时管理的轻量级线程,创建和销毁的开销非常小。

可以轻松创建成千上万个 goroutine,高效利用多核心处理器。

 

   

6. 并发中的常见问题与解决方案

6.1 数据竞争:保护共享资源

在多个 goroutine 中同时访问和修改共享资源可能引发数据竞争问题。

为了避免数据竞争,可以使用sync包中的Mutex来保护共享资源的访问。

package main
import ( "fmt" "sync")
var counter intvar mutex sync.Mutex
func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go increment(&wg) } wg.Wait() fmt.Println("Counter:", counter)}
func increment(wg *sync.WaitGroup) { defer wg.Done() mutex.Lock() counter++ mutex.Unlock()}

在这个例子中,使用了sync.Mutex 来保护counter 的访问,确保在同一时刻只有一个 goroutine 可以修改 counter 的值,避免了数据竞争问题。

6.2 死锁:避免 Goroutine 陷入无限等待

死锁是并发编程中常见的问题,它发生在两个或多个 goroutine 相互等待对方完成,导致所有 goroutine 都无法继续执行。

为了避免死锁,需要遵循一定的规则,例如避免 goroutine 间的循环依赖等。

 

   

7. 并发的最佳实践

  • 尽量使用匿名函数启动 goroutine

  • 根据系统核心数合理限制 goroutine 数量

  • 尽量避免复杂的共享状态

  • channel 及缓冲区大小适当设置

  • 合理设置并发数,避免过多上下文切换

  • 多使用 sync 等并发安全工具提高稳定性

  • 优化计算密集型任务,减少资源消耗

 

   

总结

通过这篇文章, 介绍了 Go 语言实现并发的各种模型、方式方法、优势以及应用场景。

并发是 Go 语言一个重要优势,充分利用 Go 并发编程可以让代码更加健壮与高效。

 

  阅读 182 Go先锋 ​ 喜欢此内容的人还喜欢   Go语言导出包解密:外部访问你的类型和值     Go先锋 不看的原因   Go语言排序黑魔法大公开:自定义排序新姿势!     Go先锋 不看的原因   K8s核心概念(这篇绝了)     测试开发学习交流 不看的原因   写留言            

人划线

 

标签:潜能,魔力,fmt,goroutine,并发,ch,go,Go,channel
From: https://www.cnblogs.com/cheyunhua/p/17812439.html

相关文章

  • mongoshake安装部署
    环境:Os:Centos7主库:单机的副本集模式目的库:单机的非副本集模式 1.下载地址https://github.com/alibaba/MongoShake/releases?spm=a2c6h.12873639.0.0.695e4a3dr8l0Ee 2.安装步骤我这里是安装在目的端,安装在源端机器或是单独的机器安装也是可以的[root@localhostsoft......
  • 提升应用性能:Go中的同步与异步处理
    提升应用性能:Go中的同步与异步处理原创 TimLiu 爱发白日梦的后端 2023-11-0608:00 发表于广东收录于合集#go90个爱发白日梦的后端专注Go语言领域的发展,学习成为更牛逼的架构师,日常分享Go语言、架构、软件工具的使用。124篇原创内容公众号在开......
  • Golang面试题从浅入深高频必刷「2023版」
    大家好,我是阳哥。专注Go语言的学习经验分享和就业辅导。Go语言特点Go语言相比C++/Java等语言是优雅且简洁的,是我最喜爱的编程语言之一,它既保留了C++的高性能,又可以像Java,Python优雅的调用三方库和管理项目,同时还有接口,自动垃圾回收和goroutine等让人拍案叫绝的设计。有许多基于......
  • Eolink Apikit 版本更新:数据字典功能上线、支持 MongoDB 数据库操作...
    ......
  • Django实战项目-学习任务系统-配置定时调度任务
    接着上期代码内容,继续完善优化系统功能。 本次增加配置定时调度任务功能,学习任务系统定时任务管理添加的定时学习任务,需要通过配置调度任务,定时发布周期性的学习任务。以及每天定时发送学生用户属性值,积分值等信息到学生用户知晓。以及其他需要定时调度的任务都可以配置到定时......
  • Hadoop整合AWS S3和Google gcs对象存储实践
    1.背景https://blog.51cto.com/u_15327484/8193991介绍了海外Hadoop集群一般将冷数据放入到AWSS3或者存放到Googlegcs对象存储中。这些对象存储都提供了各自的客户端进行访问,例如awss3的客户端命令就是awss3;gcs的客户端命令是gsutil。这些命令一般需要直接登陆到授权机器中执......
  • 饮冰三年-人工智能-Django淘宝拾遗-86- Django Debug Toolbar调试工具
       在开发Django应用程序时,调试是不可或缺的一部分。DjangoDebugToolbar是一个强大的调试工具,可以帮助开发者深入了解应用程序的性能和执行细节。本文将介绍DjangoDebugToolbar的安装步骤以及如何在Django应用程序中使用该工具。项目使用的是github上开源的项目......
  • Go 生成protobuf示例
    先安装好工具goinstallgoogle.golang.org/protobuf/cmd/[email protected]/grpc/cmd/protoc-gen-go-grpc@latest下载安装protocwgethttps://github.com/protocolbuffers/protobuf/releases/download/v25.0/protoc-25.0-linux-x86_64.zip......
  • Go语言使用range修改值,需要使用切片的指针 &slice[index]
    由于Value是值拷贝的,并非引用传递,所以直接改Value是达不到更改原切片值的目的的,需要通过&slice[index]获取真实的地址packagemainimport("fmt")funcmain(){ slice:=[]int{10,20,30,40} forindex,value:=rangeslice{ fmt.Printf("Value=%d,value-addr......
  • 【Go 编程实践】从零到一:创建、测试并发布自己的 Go 库
    为什么需要开发自己的Go库在编程语言中,包(Package)和库(Library)是代码组织和复用的重要工具。在Go中,包是代码的基本组织单位,每个Go程序都由包构成。包的作用是帮助组织代码,提供封装和代码复用的机制。Go包可以包含函数、类型、变量和常量等,这些元素可以被其他包引用和使用。......