Go语言的并发与管道

Go语言以其简洁的语法和强大的并发处理能力而闻名。在Go中,goroutines是并发执行的基本单位,而channels则是goroutines之间通信的管道。本篇博客将详细介绍Go语言中的并发、管道(发送、接收)、无缓冲管道、有缓冲管道、关闭管道、单向管道以及多路复用的概念,并通过实例进行说明。

并发

并发是Go语言的核心特性之一,它允许我们同时执行多个任务。在Go中,goroutines是轻量级的线程,由Go运行时管理。goroutines之间的切换非常高效,因为它们共享同一个进程的内存空间。

示例

go 复制代码
package main

import (
	"fmt"
	"time"
)

func main() {
	go func() {
		fmt.Println("zhangsan")
	}()

	fmt.Println("我是main goroutine")
	time.Sleep(1 * time.Second)
}

在这个例子中,我们启动了一个goroutine,它会打印"zhangsan",而主线程会打印"我是main goroutine"。由于goroutine的执行是并发的,所以两个打印语句的执行顺序是不确定的。

管道(Channels)

管道是Go中goroutines之间通信的机制。通过管道,goroutines可以发送和接收数据。

发送和接收

发送数据到管道使用 <- 操作符,接收数据使用 <- 操作符。

示例

go 复制代码
package main

import (
	"fmt"
)

func main() {
	ch := make(chan string)
	go func() {
		fmt.Println("zhangsan")
		ch <- "goroutine 完成"
	}()

	fmt.Println("我是main goroutine")
	v := <-ch
	fmt.Println("接收到chan中的值:", v)
}

在这个例子中,一个goroutine发送了一个字符串到管道,主线程从管道中接收并打印了这个字符串。

无缓冲管道

无缓冲管道必须在接收者准备好之前,发送者不能发送数据,否则会阻塞。

示例

go 复制代码
package main

import (
	"fmt"
)

func main() {
	ch := make(chan string)
	go func() {
		ch <- "无缓冲管道"
	}()

	v := <-ch
	fmt.Println("接收到无缓冲管道中的值:", v)
}

在这个例子中,goroutine发送数据到无缓冲管道,主线程接收数据。如果主线程没有准备好接收,goroutine将会阻塞。

有缓冲管道

有缓冲管道可以在没有接收者的情况下存储一定数量的数据。

示例

go 复制代码
package main

import (
	"fmt"
)

func main() {
	cacheCh := make(chan int, 5)
	cacheCh <- 2
	cacheCh <- 3
	fmt.Println("cacheCh 容量为:", cap(cacheCh), "元素个数为:", len(cacheCh))
	go func() {
		fmt.Println(<-cacheCh)
	}()
}

在这个例子中,我们创建了一个容量为5的有缓冲管道。我们可以向管道中发送数据,而不会阻塞,直到管道满了为止。

关闭管道

关闭管道后,不能再向管道中发送数据,但可以接收数据。如果尝试向已关闭的管道发送数据,程序会panic。

示例

go 复制代码
package main

import (
	"fmt"
)

func main() {
	cacheCh := make(chan int, 5)
	cacheCh <- 2
	cacheCh <- 3
	close(cacheCh)
	go func() {
		fmt.Println(<-cacheCh)
	}()
}

在这个例子中,我们关闭了管道,然后启动了一个goroutine来接收数据。关闭管道后,不能再向管道中发送数据。

单向管道

单向管道只能用于发送或接收,不能同时进行。

示例

go 复制代码
package main

import (
	"fmt"
)

func main() {
	onlySend := make(chan<- int)
	onlyResive := make(<-chan int)
	onlySend <- 1
	<-onlyResive
}

在这个例子中,onlySend 是一个只发送的管道,onlyResive 是一个只接收的管道。尝试向只接收的管道发送数据或从只发送的管道接收数据都会导致编译错误。

多路复用

多路复用允许我们同时从多个管道中接收数据。

示例

go 复制代码
package main

import (
	"fmt"
)

func downLoad(chanName string) string {
	time.Sleep(1 * time.Second)
	return chanName + ":filePath"
}

func main() {
	firstCh := make(chan string)
	secondCh := make(chan string)
	threeCh := make(chan string)

	go func() {
		firstCh <- downLoad("first")
	}()
	go func() {
		secondCh <- downLoad("second")
	}()
	go func() {
		threeCh <- downLoad("three")
	}()

	select {
	case v := <-firstCh:
		fmt.Println("firstCh:", v)
	case v := <-secondCh:
		fmt.Println("secondCh:", v)
	case v := <-threeCh:
		fmt.Println("threeCh:", v)
	}
}

在这个例子中,我们启动了三个goroutine,每个goroutine下载一个文件并将其路径发送到不同的管道。主线程使用select语句从这三个管道中接收数据,实现了多路复用。

通过以上示例,我们可以看到Go语言在并发处理和管道通信方面的强大能力。这些特性使得Go语言非常适合用于构建高性能的并发应用程序。

相关推荐
Harvil_1 小时前
任务跑一半,API 突然报“对话超长“断流:我给 Agent 修的紧急逃生通道
后端
用户EasyAdminBlazor1 小时前
EasyAdminBlazor SignalR 实时消息源码解析:从 NotificationHub 到站内消息
后端
程序猿乐锅1 小时前
【黑马点评 | 第十一篇】关注 Feed 流实现
java·数据库·redis·分布式·后端·缓存·maven
用户EasyAdminBlazor1 小时前
EasyAdminBlazor 定时任务:FreeScheduler 可视化调度与任务管理
后端
Evand J1 小时前
【MATLAB例程】三维快速扩展随机树(RRT)路径规划与到达角(AOA)、到达时间差(TDOA)融合定位,附下载链接
开发语言·matlab·代码·定位·例程
执明wa1 小时前
Android RecyclerView 实战:点击频道栏切换对应内容
android·开发语言·windows·microsoft·android studio
开开心心就好1 小时前
图片白底怎么去掉?抠图工具抠完背景透明
java·服务器·开发语言·pdf·ocr·散列表·启发式算法
朝朝辞暮i1 小时前
C++ 第 22 课:const —— 不允许随便修改的数据
开发语言·c++·算法
web打印社区1 小时前
JS 静默打印怎么做:纯前端为什么不行,以及最小可跑通写法
开发语言·前端·javascript·websocket·网络协议·http·pdf
大圣编蚕2 小时前
C语言怎么写小游戏?从零开始做一个贪吃蛇
c语言·开发语言