Go协程与通道的综合应用问题

Go从入门到精通(19)-协程(goroutine)与通道(channel) 作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算)和程序的并发。程序可以在不同的处理器和计算机上同时执行不同的代码段。Go 语言为构建并发程序的基本代码块是 协程 (goroutine) 与通道 (channel)。他们需要语言,编译器,和runtime的支持。Go 语言提供的垃圾回收器对并发编程至关重要。不要通过共享内存来通信,而通过通信来共享内存。通信强制协作。一个应用程序是运行在机器上的一个进程;进程是一个运行在自己内存地址空间里的独立执行体。 阅读详情

1.简单了解什么是协程和通道

什么是协程

协程,是一种用户级的轻量级的线程,拥有独立的栈空间并共享程序的堆空间。

它是在单线程的基础上通过算法来实现的微线程,相比于多线程编程具有以下优点:

  • 协程的上下文切换由用户决定,无需系统内核的上下文切换,减少开销
  • 协程默认会做好全方位保护,以防止中断。无需原子操作锁
  • 单线程也可以实现高并发,甚至达到单核CPU就支持上万协程

什么是通道

通道,是一种用于协程之间进行通信的数据结构。类似于队列,一端为发送者,一端为接收者。使用通道可以很好地保证数据的同步性和顺序性。

通道分为有缓冲通道和无缓冲通道,其声明方式如下:

  • 有缓冲通道
intChan := make(chan int,<缓冲容量>)
  • 无缓冲通道
intChan := make(chan int)

有缓冲通道和无缓冲通道的区别:

  • 阻塞:无缓冲通道发送者会阻塞直到数据被接收;有缓冲通道发送者会阻塞直到缓冲区满,接收者会阻塞直到缓冲区不为空。
  • 数据同步和顺序:无缓冲通道保证数据的同步和顺序;有缓冲管道不保证数据的同步和顺序。
  • 应用场景:无缓冲通道要求严格的同步和顺序性;有缓冲通道可以异步通信并提高吞吐量。

在无缓冲通道的实现中需要注意的是,通道的两端必须存在发送者和接收者,否则会导致死锁。

2.协程-通道并发编程案例

(1)交替打印字母和数字

题意:使用协程-通道交替打印数字1-10和字母A-J。

代码:

package main

import (
	"fmt"
	"sync"
)

/*
无缓冲chanel:需要在写入chanel的时候要保证有另外一个协程在读取chanel。否则会导致写端阻塞,发生死锁
解决办法:
避免死锁的发生:
当i循环到10时,printAlp协程已然结束,所以此时不必再写入alp通道
*/

func printNum(wg *sync.WaitGroup, numCh chan struct{}, alpCh chan struct{}) {
	defer wg.Done()

	for i := 1; i <= 10; i++ {
		<-alpCh // 等待字母goroutine发信号
		fmt.Print(i, " ")
		//避免死锁发生
		if i < 10 {
			numCh <- struct{}{} // 发信号给字母goroutine
		}
		if i == 10 {
			close(numCh)
		}
	}

}

func printAlp(wg *sync.WaitGroup, numCh chan struct{}, alpCh chan struct{}) {
	defer wg.Done()

	for i := 'A'; i <= 'J'; i++ {
		<-numCh // 等待数字goroutine发信号
		fmt.Printf("%c", i)
		alpCh <- struct{}{} // 发信号给数字goroutine
	}
	close(alpCh)
}

func main() {
	numCh := make(chan struct{}) // 用于数字goroutine的信号通道
	alpCh := make(chan struct{}) // 用于字母goroutine的信号通道
	var wg sync.WaitGroup

	wg.Add(2)

	go printAlp(&wg, numCh, alpCh)
	go printNum(&wg, numCh, alpCh)

	// 启动时先给数字goroutine发送一个信号
	numCh <- struct{}{}

	wg.Wait()

}

题目分析:

题目要求我们交替打印字母和数字,则需要保证两个协程的严格顺序性,符合无缓冲通道的应用场景。设置两个通道,分别存储数字和字母,两个打印数字和字母的协程分别担任两个通道的发送者和接收者的两重身份。循环打印一次,发一次信号,提醒另一个协程进行打印。

需要注意的是当最后一个字符'10'打印结束后,此时打印字母的协程已经结束,numCh通道已经没有接收者,此时已经不符合无缓冲通道的实现条件-必须存在发送者和接收者,再发送信号,会引起阻塞死锁。所以再第10次时不必再发送信号。

(2)设计一个任务调度器

题目:设计一个任务调度器,利用多协程+通道的编程模式,实现并发处理多任务的业务场景,且要求调度顺序按照任务添加顺序。

代码:

type scheduler struct {
	taskChan chan func()
	wt       sync.WaitGroup
}

func (td *scheduler) AddTask(task func()) {
	td.taskChan <- task
}

func (td *scheduler) Executer() {
	defer td.wt.Done()
	for {
		task, ok := <-td.taskChan
		task()
		if ok && len(td.taskChan) == 0 {
			break
		}
	}
}

func (td *scheduler) Start() {
	td.wt.Add(4)
	//假设四个消费者
	for i := 0; i < 4; i++ {
		go td.Executer()
	}

	td.wt.Wait()
}

func main() {
	sd := scheduler{
		taskChan: make(chan func(), 5),
	}

	go func() {
		sd.AddTask(func() {
			fmt.Println("任务1")
		})
		sd.AddTask(func() {
			fmt.Println("任务2")
		})
		sd.AddTask(func() {
			fmt.Println("任务3")
		})
		sd.AddTask(func() {
			fmt.Println("任务4")
		})
		sd.AddTask(func() {
			fmt.Println("任务5")
		})
		sd.AddTask(func() {
			fmt.Println("任务6")
		})
		close(sd.taskChan)
	}()

	sd.Start()

}

问题分析:

由于添加的任务为多任务,不止一个,并且需要异步处理执行这些任务。符合有缓冲区的通道需要提高吞吐量和异步处理。

那么,我们需要将任务放进通道,多个接收者,按照顺序从通道中拿任务,并进行执行即可。

需要注意的问题是,如果在添加的任务数量大于通道的缓冲区,会在添加任务形成阻塞。为了不影响消费者的正常启动,需要将其单独开一个协程来添加任务。

这样当消费者进行消费时,形成阻塞的生产者会被唤醒,从而继续进行任务添加。

3.总结

经过对协程+通道的编程模式的学习,除了刚刚在题目中提到的,我们还应该注意以下问题:

1.为什么通道用完之后要关闭,不关闭有什么风险?

  • 为了避免死锁。关闭通道,也是告诉接收者,在发送者那里已经没有数据可以发送了,不需要再继续等待数据了。接收者收到通道关闭的信息后,停止接收数据;若不关闭通道,则会让接收者一直处于阻塞状态,有发生死锁的风险。
  • 释放资源和避免资源泄露。关闭通道后,系统会释放相应的资源,及时关闭通道则可以避免资源浪费和泄露。 

2. 怎么优雅地关闭通道?

首先,关闭通道的最基本原则是不要关闭已经关闭的通道。其次还有一个使用Go通道的原则:不要在数据接收方或者在有多个发送者的情况下关闭通道。换句话说,我们只应该让一个通道唯一的发送者关闭此通道。

一种粗鲁的方式是通过异常恢复的方式来关闭通道,但很明显违反以上的原则且有可能发生数据竞争;另一种方式是sync.Once或sync.Mutex来关闭通道,当并不保证发生在一个通道上的并发关闭操作和发送操纵不会产生数据竞争。这两种方式都有一定的问题,就不过多介绍,下面介绍一种如何优雅地关闭通道的方法。

情形一:M个接收者和一个发送者

最容易处理的一种情形。当发送者需要结束发送时,让它关闭通道即可。上文的两个编程案例就是这种情形。

情形二:一个接收者和N个发送者

根据Go通道的基本原则,我们只能在通道的唯一发送者关闭通道。所以,在这种情况下,我们无法直接在某处关闭通道。但我们可以让接收者关闭一个额外的信号通道来告诉发送者不要再发送数据了。

package main

import (
	"log"
	"sync"
)

func main() {

	cosnt N := 5
	cosnt Max := 60000
	count := 0

	dataCh := make(chan int)
	stopCh := make(chan bool)

	var wt sync.WaitGroup
	wt.Add(1)

	//发送者
	for i := 0; i < N; i++ {
		go func() {
			for {
				select {
				case <-stopCh:
					return
				default:
					count += 1
					dataCh <- count
				}
			}
		}()
	}

	//接收者
	go func() {
		defer wt.Done()
		for value := range dataCh {
			if value == Max {
				// 此唯一的接收者同时也是stopCh通道的
				// 唯一发送者。尽管它不能安全地关闭dataCh数
				// 据通道,但它可以安全地关闭stopCh通道。
				close(stopCh)
				return
			}
			log.Println(value)
		}
	}()

	wt.Wait()
}

在这种方法中,我们额外增加了一个信号通道stopCh,在接收方通过它来告诉发送者不必再接收数据。并且,此方法并没有对dataCh进行关闭,当一个通道不再被任何协程使用时,它将会逐渐被垃圾回收掉,无论它是否已经被关闭。

此方法的优雅性就在于通过关闭一个通道来停止另一个通道的使用,从而间接关闭另一个通道。

情形三:M个接收者N个发送者

我们不能让接收者和发送者中的任何一个关闭用来传输数据的通道,我们也不能让多个接收者之一关闭一个额外的信号通道。这两种做法都违反了通道关闭原则。

不过,我们可以引入一个中间调解者角色并让其关闭额外的信号通道来通知所有接收者和发送者结束工作。

代码示例:

package main

import (
	"log"
	"math/rand"
	"strconv"
	"sync"
)

func main() {

	const Max = 100000
	const NumReceivers = 10
	const NumSenders = 1000

	var wt sync.WaitGroup
	wt.Add(NumReceivers)

	dataCh := make(chan int)
	stopCh := make(chan struct{})
	// stopCh是一个额外的信号通道。它的发送
	// 者为中间调解者。它的接收者为dataCh
	// 数据通道的所有的发送者和接收者。
	toStop := make(chan string, 1)
	// toStop是一个用来通知中间调解者让其
	// 关闭信号通道stopCh的第二个信号通道。
	// 此第二个信号通道的发送者为dataCh数据
	// 通道的所有的发送者和接收者,它的接收者
	// 为中间调解者。它必须为一个缓冲通道。

	var stoppedBy string

	// 中间调解者
	go func() {
		stoppedBy = <-toStop
		close(stopCh)
	}()

	// 发送者
	for i := 0; i < NumSenders; i++ {
		go func(id string) {
			for {
				value := rand.Intn(Max)
				if value == 0 {
					// 为了防止阻塞,这里使用了一个尝试
					// 发送操作来向中间调解者发送信号。
					select {
					case toStop <- "发送者:" + id:
					default:
					}
					return
				}

				select {
				case <-stopCh:
					return
				case dataCh <- value:
				}
			}
		}(strconv.Itoa(i))
	}

	// 接收者
	for i := 0; i < NumReceivers; i++ {
		go func(id string) {
			defer wt.Done()

			for {
				select {
				case <-stopCh:
					return
				case value := <-dataCh:
					if value == Max {
						// 发送操作来向中间调解者发送信号。
						select {
						case toStop <- "接收者:" + id:
						default:
						}
						return
					}

					log.Println(value)
				}
			}
		}(strconv.Itoa(i))
	}

	wt.Wait()
	log.Println("被" + stoppedBy + "终止了")

}

AMBA-AXI and ACE协议详解(十) 本章介绍了ACE协议中的Barrier事务机制,包括内存屏障和同步屏障两种类型。 阅读详情

相关推荐

LSS(Lift, Splat, Shoot)算法解析

**LSS(Lift, Splat, Shoot)** 是一个比较经典的自下而上的构建BEV特征的3D目标检测算法,通过将图像特征反投影到3D空间生成伪视锥点云,通过`Efficientnet`算法提取云点的深度特征和图像特征并对深度信息进行估计,最终将点云特征转换到BEV空间下进行特征融合和后续的语义分割任务。 本篇按照LSS代码的运行逻辑,结合论文对LSS算法进行解析。

超爱吃小蛋糕的66的博客 8679

Go中Goroutine和通道常见场景

本篇给出4个场景,这4个场景是在运维开发工作中较为常见的且也是比较典型的场景。总而言之,言而总之,当涉及到处理并发和并行任务时,Goroutine和通道是非常强悍的,可以让我们开发出高效的、牛逼的并发程序。上面的代码,创建了一个Goroutine池,每个Goroutine代表一台服务器,通过通道将任务分发给Goroutine进行并发执行。在上面的代码中,使用了一个专门的Goroutine来处理日志写入操作,该Goroutine从一个日志通道中读取日志数据,并将其写入到目标中。

Leckun的专栏 354

Python自动化生成HyperMesh Tcl命令流:从几何建模到有限元分析

本文详细介绍了如何利用Python自动化生成HyperMesh的Tcl命令流,实现从几何建模到有限元分析的全流程二次开发。通过封装常用操作函数,工程师可以高效完成参数化建模、自动网格划分及求解器文件输出,显著提升CAE前处理效率与标准化水平。

weixin_29041767的博客 423

Go channel的使用场景,用法总结

在介绍10种操作前,先简要介绍下channel的使用场景、基本操作和注意事项。 channel的使用场景 把channel用在数据流动的地方: 消息传递、消息过滤 信号广播 事件订阅与广播 请求、响应转发 任务分发 结果汇总 并发控制 同步与异步 ... channel的基本操作和注意事项 channel存在3种状态: nil,未初始化的状态,只进行了声明,或者手动赋值为nil active,正常的channel,可读或者可写 closed,已关闭,千万不要误认为关闭chann

Mark66890620 2793

点云数据生成DEM,并导出三维模型

点云数据生成DEM,并导出三维模型

qq_42540466的博客 2万+

【推荐100个unity插件之2】 DoTween动画插件的安装和使用整合(最全)

摘要:DOTween动画插件使用指南 DOTween是Unity中强大的动画插件,比原生动画更便捷。安装后需进行初始化设置,通过工具菜单生成程序集。主要提供通用方法(DOTween.To)和便捷方法(如transform.DOMove)两种动画实现方式。插件支持丰富的动画设置(循环、缓动等)和事件回调(OnComplete、OnKill)。还提供动画管理功能,包括清除(DOTween.Clear)、完成(DOTween.CompleteAll)和终止(DOTween.KillAll)动画。常用功能涵盖移动、

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1万+

Go 语言通道 (Channel) 深度用法讲解及实战

通道(Channel)是 Go 语言实现 goroutine 间通信的核心机制,也是实现 “不要通过共享内存通信,而要通过通信共享内存” 这一设计哲学的关键。除了基础的读写操作,通道还有很多深度用法,能优雅解决并发同步、限流、任务分发等问题。

2401_89221445的博客 572

风机叶片小规模振动监测数据集--读取测试

ETH风电叶片缺陷数据集–读取测试 数据简介 提示:这里可以添加本文要记录的大概内容: 结构健康监测是保障重要机电设备、建筑设施健康的重要技术手段。 一、lvm_read包安装 示例:pandas 是基于NumPy 的一种工具,该工具是为了解决数据分析任务

cnjs1994的博客 2850

深入理解Go通道(Channel)

核心定义:通道是Go语言中用于 goroutine 之间传递数据和同步执行的特殊数据结构,可理解为“goroutine 之间的通信管道”。设计初衷:解决多 goroutine 并发时的共享资源竞争问题,通过“通信”替代“共享内存”,降低并发编程复杂度。通道是 Go 并发通信的核心,遵循“通信优先于共享内存”的设计哲学。核心区分:无缓冲通道(同步)、有缓冲通道(异步),需结合场景选择。进阶用法:方向限制、for range 遍历、select 多路复用,是解决复杂并发问题的关键。

qq_40230623的博客 1315

24、水下传感器网络中混合LEACH协议与遗传算法获取路由节点

本文提出了一种结合遗传算法与LEACH协议的混合路由方案,用于提升水下无线传感器网络(UWSN)的能量利用效率和数据传输稳定性。通过遗传算法优化簇头选择与路径规划,有效避免节点过早死亡,减少能量消耗,在200个节点场景下显著提升网络寿命。相比传统LEACH协议,该方案具有更高的能量利用率、更强的环境适应性,并支持传输历史记录与并行传输序列。实验结果显示,该方案在总能量为2000焦耳时剩余能量达75.6%,远优于LEACH的34.8%能耗表现。适用于海洋监测、石油平台监控及水下灾害预警等复杂环境应用。

i8j9k0l1的博客 112

​热电偶测温电路图(一)​

高精度热电偶测温电路图 此设计概括了提供传感器诊断所必需的抗混叠滤波器和偏置电阻器。此示例还提供了一种新颖的方式,即使用ADS1118上的板载温度传感器完成对系统的冷端补偿。对于热电偶线性化,此设计还提供了一种非常简单的、可以在大多数微控制器上实施的线性算法。 缺点就是太贵。 特性 测量K型热电偶温度 精度1°C 高精度/可重复性0.2°C 包含冷端补偿 包含软件算法 使用ADS111816位ADC(带PGA) 原理图/方框图 热电偶测温电路图(二) OP07构成的高稳定热电

zxm8513优快云博客 2万+

go语言通道有哪些应用

go语言通道有哪些应用

huan 373

【人工智能】AI驱动值班巡检Agent(Hermes)

传统运维值班巡检场景中,存在四大核心痛点:本项目基于 Agent+Skills+浏览器自动化 架构,构建了具备自我进化能力的AI值班员,核心逻辑流如下:

奶爸的编程之路,也就一周冷个三天 177

【魅力golang】之-通道

昨天发布了golang的最大特色之一--协程,与协程密不可分的是通道(channel),用来充当协程间相互通信的角色。通道是一种内置的数据结构,所以才造就了golang强大的并发能力。Golang 中的通道是实现协程间通信的核心工具,简化了并发编程的复杂性。通道通过 CSP 模型实现协程间的数据传递,在消息队列、协程同步、超时控制等场景中表现出色。通过通道,协程可以安全、同步地交换数据,避免直接操作共享内存带来的复杂性。通道可以实现简单的消息队列,用于协程间的任务分发。:Go 的通道实现了。

网络风云的博客 5983

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Go通道机制与应用详解

本文深入探讨了Go语言中通道(Channel)的各个方面,从基础概念到高级应用。文章详细解析了通道的类型、操作方法以及垃圾回收机制,更进一步通过具体代码示例展示了通道在数据流处理、任务调度和状态监控等多个实际应用场景中的作用。本文旨在为读者提供一个全面而深入的理解,以更有效地使用Go中的通道进行并发编程。

TechLead 2496

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

『GoLang』协程与通道

Go语言协程与通道的初始,新手其实这么多都嫌多了,知道基本用法就好,然后以后遇到了,要理解PV操作原理再根据文档写代码会很轻松,这种不能硬着头皮学

七秒钟的记忆 957

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

go 协程与通道

go 协程与通道

wgchen 742

go通道与协程

协程 在 Go 中,应用程序并发处理的部分被称作 goroutines(协程) ,它可以进行更有效的并发运算。 操作系统会在物理处理器上调度线程来运行,而 Go 语言的运行时会在逻辑处理器上调度 goroutine来运行。每个逻辑处理器都分别绑定到单个操作系统线程。 协程是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程 最重要的是,协程不是被...

幸福诗歌的博客 604

Go协程揭秘:轻量、并发与性能的完美结合

Go协程为并发编程提供了强大的工具,结合轻量级、高效的特点,为开发者带来了独特的编程体验。本文深入探讨了Go协程的基本原理、同步机制、高级用法及其性能与最佳实践,旨在为读者提供全面、深入的理解和应用指导。

TechLead 2618

GoLang协程与通道---上

一个应用程序是运行在机器上的一个进程;进程是一个运行在自己内存地址空间里的独立执行体。一个进程由一个或多个操作系统线程组成,这些线程其实是共享同一个内存地址空间的一起工作的执行体。几乎所有’正式’的程序都是多线程的,以便让用户或计算机不必等待,或者能够同时服务多个请求(如 Web 服务器),或增加性能和吞吐量(例如,通过对不同的数据集并行执行代码)。一个并发程序可以在一个处理器或者内核上使用多个线程来执行任务,但是只有同一个程序在某个时间点同时运行在多核或者多处理器上才是真正的并行。...

m0_53157173的博客 788

Go语言协程通道使用问题

总之,虽然 select 和 time.After 的模式简单直接,但在需要真正取消操作或在多个 goroutine 间协调的场景中,使用 context 包是更好的选择。在使用 context 的版本中,如果超时发生,操作会被主动取消,避免了资源浪费。Context 包的引入大大简化了在 Go 程序中处理取消、超时和跨调用边界传值的复杂性,是构建健壮的并发和分布式系统的重要工具。是实现优雅取消和超时处理的关键机制,它允许 Go 程序以非阻塞的方式响应取消信号,从而编写出更加健壮和响应式的并发代码。

2301_76723322的博客 1807

GoLang协程与通道---下

假设我们需要处理很多任务;一个worker处理一项任务。任务可以被定义为一个结构体(具体的细节在这里并不重要): 旧模式:使用共享内存进行同步由各个任务组成的任务池共享内存;为了同步各个worker以及避免资源竞争,我们需要对任务池进行加锁保护: sync.Mutex:它用来在代码中保护临界区资源:同一时间只有一个go协程(goroutine)可以进入该临界区。如果出现了同一时间多个go协程都进入了该临界区,则会产生竞争:Pool结构就不能保证被正确更新。在传统的模式中(经典的面向对象的语言中应用得比较多,

m0_53157173的博客 1072

十四、Golang协程,通道详解

(Process)就是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。一个进程至少有 5 种基本状态,它们是:初始态,执行态,等待状态,就绪状态,终止状态。通俗的讲进程就是一个正在执行的程序。是进程的一个执行实例,是程序执行的最小单元,它是比进程更小的能独立运行的基本单位;一个进程可以创建多个线程,同一个进程中的多个线程可以并发执行,一个程序要运行的话至少有一个进程。

阿澈不吃蒜的博客 2271

Go协程机制原理

特性协程的通道操作系统的管道应用领域单进程内的并发任务进程间通信(IPC)通信方式消息传递(同步或异步)文件描述符(阻塞式)实现机制编程语言或库实现(用户空间)操作系统内核实现(系统调用)性能特点高效,无锁,适合高并发较低,涉及内核切换和数据拷贝资源占用轻量级较重,依赖内核资源灵活性支持复杂通信模式(如多对多)功能简单,通常是单向的跨平台性依赖语言或库的实现操作系统标准机制特性同步编程异步编程任务执行方式顺序执行,阻塞并发执行,非阻塞性能较低较高复杂度较低较高实现机制。

敖光 SRE 1038

golang中创建协程以及协程间的相互通信

golang中创建协程以及协程间的相互通信。主要介绍管道通信机制,make(chan)以及同步原语sync.waitgroup

weixin_60214397的博客 2211

golang协程goroutine教程

协程是计算机程序的一类组件,推广了协作式多任务的子例程,允许执行被挂起与被恢复。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。协程更适合于用来实现彼此熟悉的程序组件,如协作式多任务、异常处理、事件循环、迭代器、无限列表和管道。协程的创建和切换开销更小,因此可以创建更多的协程。协程之间是异步的,也就是说,一个协程的执行不会阻塞另一个协程的执行。协程的调度是由系统来进行的,程序员不需要手动进行调度。可以实现高效的并发编程。可以简化并发编程的代码。

dennis的博客 2313
上一篇: Go语言中的go.mod与go.sum
下一篇: 计算机网络第一章-计算机网络的基本认识
看繁星aa
博客等级 码龄4年 922粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值