Go语言的并发编程

Go 学习笔记 · 进阶篇 · 第二天:并发编程深度实践 Go 的并发是轻量高效的,Goroutine 比线程香。Channel 是 goroutine 之间的传声筒。WaitGroup、Mutex、Context 是并发控制的三剑客。并发编程容易埋雷,写完一定记得加-race运行!今日咒语:“人生苦短,让 goroutine 去忙,主程序才轻松。 阅读详情

并发指在同一时间内可以执行多个任务。并发编程含义比较广泛,包含多线程编程、多进程编程及分布式程序等。

Go语言里的并发指的是能让某个函数独立于其他函数运行的能力。当一个函数创建为协程(goroutine)时,Go语言会将其视为一个独立的工作单元,这个单元会被调度到可用的逻辑处理器上执行。

并发编程基础

1、并发与并行(理论没看懂)

理解操作系统的线程(thread)和进程(process),有助于理解Go语言运行时调度器如何得用操作系统来并发运行goroutine。

并行是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了。

使用较少的资源做更多的事情,是指导Go语言设计的哲学。

2、指定使用核心数

Go语言默认会调用CPU核心数,这些功能都是自动调整的,但是也提供了相应的标准库来指定核心数。使用flags包可以调整程序运行时调用的CPU核心数。

代码如下:

package main

import (
	"fmt"
	"time"
)

func longWait() {
	fmt.Println("开始longWait()")
	time.Sleep(5 * 1e9)
	fmt.Println("结束longWait()")
}

func shortWait() {
	fmt.Println("开始shortWait()")
	time.Sleep(2 * 1e9)
	fmt.Println("结束shortWait()")
}

func main() {
	fmt.Println("这里是main()开始的地方")
	go longWait()
	go shortWait()
	fmt.Println("挂起main()")
	time.Sleep(10 * 1e9)
	fmt.Println("这里是main()结束的地方")
}

main()、longWait()和shortWait()三个函数作为独立的处理单元按顺序启动,然后开始并行运行,每一个函数都在运行的开始和结束阶段输出了消息。

运行结果如下:

这里是main()开始的地方
挂起main()
开始longWait()
开始shortWait()
结束shortWait()
结束longWait()
这里是main()结束的地方

如果移除go语言关键字同,重新运行程序:

这里是main()开始的地方
开始longWait()
结束longWait()
开始shortWait()
结束shortWait()
挂起main()
这里是main()结束的地方

协程(goroutine)

操作系统自己掌管的进程(process)、进程内的线程(thread)以及进程内的协程(coroutine,也叫轻量级线程)。

协程的最大优势在于其“轻量级”,可以轻松创建上百万个协程而不会导致系统资源衰竭,而线程和进程通常最多也不能超过1万个。

Go语言在语言级别支持轻量级线程,叫goroutine。

1、协程基础

goroutine是Go语言并行设计的核心。

goroutine是通过Go程序的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。

代码如下:

package main

import (
	"fmt"
	"runtime"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		runtime.Gosched()
		fmt.Println(s)
	}
}

func main() {
	go say("world")
	say("hello")
}

运行结果如下:与书本不一样。

hello
hello
hello
hello
hello

书本是:

hello
world
hello
world
hello
world
hello
world
hello

并发处理:Go例程,代码如下:

package main

import (
	"fmt"
)

func routine1(index int) {
	for i := 0; i < 10; i++ {
		fmt.Println(index, " : ", i)
	}
}
func main() {
	go routine1(1)
	go routine1(2)
	for i := 0; i < 10; i++ {
		fmt.Println("0: ", i)
	}
}

运行结果如下:(感觉不对,不明白什么意思)

0:  0
0:  1
0:  2
0:  3
0:  4
0:  5
0:  6
0:  7
0:  8
0:  9

2、协程间通信

通常有两种最常见的并发通信模型:共享数据和消息。

代码如下:

package main

import (
	"fmt"
	"runtime"
	"sync"
)

var counter int = 0

func Count(lock *sync.Mutex) {
	lock.Lock()
	counter++
	fmt.Println(z)
	lock.Unlock()
}

func main() {
	lock := &sync.Mutex{}
	for i := 0; i < 10; i++ {
		go Count(lock)
	}
	for {
		lock.Lock()
		c := counter
		lock.Unlock()
		runtime.Gosched()
		if c >= 10 {
			break
		}
	}
}

运行结果如下:

# command-line-arguments
./main.go:14:14: undefined: z

不要通过共享内存来通信,而应该通过通信来共享内存。

package main

import "fmt"

func Add(x, y int) {
	z := x + y
	fmt.Println(z)
}

func main() {
	for i := 0; i < 10; i++ {
		go Add(i, i)
	}
}

无运行结果。

通道(Channel)

代码如下:

package main

import (
	"fmt"
)

func Count(ch chan int) {
	ch <- 1
	fmt.Println("Counting")
}

func main() {
	chs := make([]chan int, 10)
	for i := 0; i < 10; i++ {
		chs[i] = make(chan int)
		go Count(chs[i])
	}
	for _, ch := range chs {
		<-ch
	}
}

1、基本语法

一般channel的声明形式为:

var chanName chan ElementType

在channel的用法中,最常见的包括写入和读出。

将一个数据写入(发送)到channel的语法很直观:

ch <- value

从channel中读取数据的语法是:

value:=<-ch

2、select

Go语言直接在语言级别支持select关键字,用于处理异步I/O问题。

3、缓冲机制

4、超时和计时器

5、channel的传递

6、单向channel

7、关闭channel

使用go语言内置的close()函数如下:

close(ch)

并发进阶

1、多核并行化

2、协程同步

只有在当需要告诉接受者不会再提供新的值的时候,才需要关闭通道。

只有发送者需要关闭通道,接收都永远不会需要。

代码如下:

package main

import (
	"fmt"
	"time"
)

func sendData(ch chan string) {
	ch <- "纽约"
	ch <- "华盛顿"
	ch <- "伦敦"
	ch <- "北京"
	ch <- "东京"
}

func getData(ch chan string) {
	var input string
	for {
		input = <-ch
		fmt.Printf("%s ", input)
	}
}

func main() {
	ch := make(chan string)
	go sendData(ch)
	go getData(ch)
	time.Sleep(1e9)
}

运行结果如下:

纽约 华盛顿 伦敦 北京 东京 

代码如下:

package main

import (
	"fmt"
)

func sendData(ch chan string) {
	ch <- "纽约"
	ch <- "华盛顿"
	ch <- "伦敦"
	ch <- "北京"
	ch <- "东京"
	close(ch)
}

func getData(ch chan string) {
	for {
		input, open := <-ch
		if !open {
			break
		}
		fmt.Printf("%s ", input)
	}
}

func main() {
	ch := make(chan string)
	go sendData(ch)
	getData(ch)
}

运行结果如下:

纽约 华盛顿 伦敦 北京 东京 

为一个普通函数创建goroutine的写法如下:

go 函数名(参数列表)

函数名:要调用的函数名。

参数列表:调用函数需要传入的参数。

使用go关键字创建goroutine时,被调用函数的返回值会被忽略。

代码如下:

package main

import (
	"fmt"
	"time"
)

func running() {
	var times int
	//构建一个无限循环
	for {
		times++
		fmt.Println("tick", times)
		//延时1秒
		time.Sleep(time.Second)
	}
}

func main() {
	//并发执行程序
	go running()
	//接受命令行输入,不做任何事情
	var input string
	fmt.Scanln(&input)
}

使用匿名函数创建goroutine的格式

go func (参数列表) {

        函数体

} (调用参数列表)

参数列表:函数体内的参数变量列表

函数体:匿名函数的代码。

调用参数列表:启动goroutine时,需要向匿名函数传递的调用函数。

代码如下:

package main

import (
	"fmt"
	"time"
)

func main() {

	go func() {
		var times int
		for {
			times++
			fmt.Println("tick", times)
			time.Sleep(time.Second)
		}
	}()
	var input string
	fmt.Scanln(&input)
}

调整并发的运行恒通(GOMAXPROCS)

格式如下:

runtime.GOMAXPROCS(逻辑CPU数量)

这里的逻辑CPU数量可以有如下几种数值:

<1:不修改任何数值。

=1:单核心执行。

>1:多核并发执行。

一般情况下,可以使用funtime.NumCPU()查询CPU数量 ,并使用runtime.GOMAXPROCS()函数进行设置,例如:

runtime.GOMAXPROCS(runtime.NumCPU())

理解并发和并行

并发(concurrency):把任务在不同的时间点交给处理器进行处理。在同一个时间点,任务并不会同时运行。

并行(parallelism):把每一个任务分配给每一个处理器独立完成。在同一个时间点,任务一定是同时运行。

代码如下:

package main

import (
	"fmt"
)

func sum(values []int, resultChan chan int) {
	sum := 0
	for _, value := range values {
		sum += value
	}
	resultChan <- sum		//将计算结果发送到channel中
}

func main() {
	values := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	resultChan := make(chan int, 2)
	go sum(values[:len(values)/2], resultChan)
	go sum(values[len(values)/2:], resultChan)
	sum1, sum2 := <-resultChan, <-resultChan		//接收结果
	fmt.Println("Result:", sum1, sum2, sum1+sum2)
}

运行结果如下:

Result: 40 15 55

去程:

代码如下:

package main

import (
	"fmt"
	"time"
)

func sheep(i int) {
	for ; ; i += 2 {
		fmt.Println(i, "只羊")
	}
}

func main() {
	go sheep(1)
	go sheep(2)
	time.Sleep(time.Millisecond)
}

运行结果如下:

2 只羊
4 只羊
6 只羊
8 只羊
10 只羊
12 只羊
14 只羊
16 只羊
18 只羊
20 只羊
22 只羊
24 只羊
26 只羊
28 只羊
30 只羊
32 只羊
34 只羊
36 只羊
38 只羊
40 只羊
42 只羊
44 只羊
46 只羊
48 只羊
50 只羊
52 只羊
54 只羊
56 只羊
58 只羊
60 只羊
62 只羊
64 只羊
66 只羊
68 只羊
70 只羊
72 只羊
74 只羊
76 只羊
78 只羊
80 只羊
82 只羊
84 只羊
86 只羊
88 只羊
90 只羊
92 只羊
94 只羊
96 只羊
98 只羊
100 只羊
102 只羊
104 只羊
106 只羊
108 只羊
110 只羊

回程:

代码如下:

package main

import (
	"fmt"
	"time"
)

var wormhole chan time.Time

func deepspace() {
	wormhole <- time.Now()
}

func main() {
	wormhole = make(chan time.Time)
	go deepspace()
	fmt.Println(<-wormhole)
}

运行结果如下:

2022-04-06 15:47:02.3888929 +0800 CST m=+0.002617601

遍历与关闭:

代码如下:

package main

import (
	"fmt"
	"time"
)

type hole chan time.Time

func deepspace(w hole, h int) {
	defer close(w)
	for ; h > 0; h-- {
		w <- time.Now()
		time.Sleep(time.Second)
	}
}

func consumer(w hole) {
	for msg := range w {
		fmt.Println("consumer", msg)
	}
}

func main() {
	w := make(hole)
	go deepspace(w, 8)
	go consumer(w)
	for {
		msg, ok := <-w
		if !ok {
			break
		}
		fmt.Println("main", msg)
	}
	fmt.Println("Done")
}

运行结果如下:

main 2022-04-09 14:52:07.041789 +0800 CST m=+0.002800701
consumer 2022-04-09 14:52:08.0456268 +0800 CST m=+1.006638501
main 2022-04-09 14:52:09.0578227 +0800 CST m=+2.018834401
consumer 2022-04-09 14:52:10.0705782 +0800 CST m=+3.031589901
main 2022-04-09 14:52:11.0742367 +0800 CST m=+4.035248401
consumer 2022-04-09 14:52:12.0782694 +0800 CST m=+5.039281101
main 2022-04-09 14:52:13.0930031 +0800 CST m=+6.054014801
consumer 2022-04-09 14:52:14.0936872 +0800 CST m=+7.054698901
Done

MapReduce:

映射化简:每个去程对应一个Map并发执行,结果写入缓冲程道,最后再用一个Reduce函数综合这个程道得到的每一个值。

代码如下:

package main

import (
	"fmt"
	"math/rand"
)

type hole chan int

func deepspace(w hole) {
	w <- rand.Int()
}

func main() {
	n := 8
	w := make(hole, n)
	//Map
	for i := 0; i < n; i++ {
		go deepspace(w)
	}
	//Reduce
	t := 0
	for i := 0; i < n; i++ {
		t += <-w
	}
	fmt.Println("Total:", t)
}

运行结果 如下:

Total: -174686420686987236

两个goroutine进行并行的累加计算:代码如下:

package main

import (
	"fmt"
)

func sum(values []int, resultChan chan int) {
	sum := 0
	for _, value := range values {
		sum += value
	}
	resultChan <- sum
}

func main() {
	values := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
	resultChan := make(chan int, 2)
	go sum(values[:len(values)/2], resultChan)
	go sum(values[len(values)/2:], resultChan)
	sum1, sum2 := <-resultChan, <-resultChan
	fmt.Println("Result:", sum1, sum2, sum1+sum2)
}

运行结果如下:

Result: 40 15 55

select语句:

代码如下:

package main

import (
	"fmt"
	"math/rand"
	"time"
)

type hole chan int

func deepspace(w hole, h int) {
	defer close(w)
	d := time.Duration(rand.Intn(h)) * time.Second
	for ; h > 0; h-- {
		w <- rand.Int()
		time.Sleep(d)
	}
}

func main() {
	n := 8
	w := make(hole)
	t := 0
	maxTime := time.Second
	go deepspace(w, n)
Out:
	for i := 0; i < n; i++ {
		select {
		case n := <-w:
			t += n
		case <-time.After(maxTime):
			fmt.Println("Time out")
			break Out
		}
	}
	fmt.Println("Total:", t)
}

运行结果如下:

Time out
Total: 8674665223082153551

程道值:

代码如下:

package main

import (
	"fmt"
)

type Read struct {
	key   string
	reply chan<- string
}

type Write struct {
	key string
	val string
}

var hole = make(chan interface{})

func deepspace() {
	m := map[string]string{}
	for {
		switch r := (<-hole).(type) {
		case Read:
			r.reply <- m[r.key] + " from Mars."
		case Write:
			m[r.key] = r.val
		}
	}
}

func main() {
	go deepspace()
	hole <- Write{"Name", "Martin"}
	home := make(chan string)
	hole <- Read{"Name", home}
	fmt.Println(<-home)
}

运行结果如下:

Martin from Mars.

Go语言学习笔记—golang并发编程之channel 视频来源于B站Go语言基础进阶视频av56860636 文章为自己整理的学习笔记,侵权即删,谢谢支持! 阅读详情

相关推荐

Gradle进阶:4:结合Sonarqube进行代码审查

作为代码质量检查的流行工具,比如Sonarqube能够检查代码的“七宗罪”,跟代码结合起来能够更好地提高代码的质量,这篇文章将会介绍如何结合gradle和sonarqube对代码质量与测试覆盖率进行分析。

知行合一 止于至善 1万+

Go 并发编程

关于 Go 语言之高性能并发编程的基础知识!

qq_46457076的博客 1580

代码生成 | 安时积分法模型搭建

安时积分法是电池电量计量最基础的方法,今天我们用simulink建模的方式做一个安时积分模型,从而更好地理解安时积分的思想也掌握建模的基础操 ​新建文件 打开MATLAB启动simulink新建一个模型文件 定义变量 和手写代码一样,先定义几个后面要用到的变量,在simulink建模时新建变量用Data Store Memory模块。双击填入要定义的变量名,然后在signal attributes赋初值 如此方法新建四个变量current(电流)初值1000mA、tatol_cap(总容量

ningvay的博客 1万+

Go语言并发编程

go语言并发编程、加锁、并发。

weixin_46042817的博客 1379

射频功率放大器设计实战:从理论到Layout的全流程解析

本文以一款40W、960MHz LDMOS功率放大器为例,系统解析了从理论分析、仿真设计到版图实现的射频功率放大器设计全流程。内容涵盖AB类放大器选型、核心指标定义、稳定性分析、Load-Pull/源牵引仿真、史密斯圆图匹配、偏置网络设计,以及最终的谐波平衡仿真与版图电磁验证,为工程师提供了一套从理论到实践的完整设计方法论。

weixin_29266007的博客 249

IP2315高效同步降压充电IC在快充应用中的硬件与软件控制策略

本文深入解析IP2315高效同步降压充电IC在快充应用中的硬件与软件控制策略,涵盖其核心特性、硬件电阻调节技巧、I2C动态控制方案、散热设计优化及典型问题排查。IP2315支持多种快充协议,如Type-C PD、华为FCP等,适用于便携设备快速充电需求,并通过I2C实现灵活的充电参数设置,提升充电效率与安全性。

weixin_29248313的博客 539

GO并发编程

无缓冲channel:本质是一种同步通道,他要求读写操作紧密进行。所以,在单channel下,如果在无缓冲channel中进行读写,此时就会在第一次操作处阻塞,发生死锁。

2303_79380171的博客 1638

【linux】解决报错:Network error: Connection refused

【linux】解决报错:Network error: Connection refused

weixin_44502754的博客 4936

Go语言并发详解

文章目录一、Go语言并发简述(并发的优势)进程/线程并发/并行协程/线程Goroutine 介绍channel二、Go语言goroutine(轻量级线程)使用普通函数创建 goroutine1) 格式2) 例子使用匿名函数创建goroutine1) 使用匿名函数创建goroutine的格式2) 使用匿名函数创建goroutine的例子提示三、Go语言并发通信四、Go语言竞争状态简述Goroutine 7 (finished) created at: main.main() D:/code/src/m

qq_41854911的博客 1万+

水声信号处理开源算法库最新进展

欢迎关注本专栏,一起探索新世界大家好,我是ZhuChunSHU。985院校硕士毕业,现担任算法研究员一职,热衷于深度学习算法研究与应用。曾获得阿里云天池比赛第三名,CCE比赛第五名,科大讯飞Q比赛第六名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法行业就业。希望和大家一起成长进步。今天继续我们的专栏分享!!!!!!!!!!!!!!!!!!!!水声信号处理是海洋科学与工程领域的核心组成部分,对于水下通信、目标探测、海洋环境监测以及生物声学研究至关重要。

u012133341的博客 1254

Plant Simulation 双深位立库开发系列教程-货架参数化

为了方便用户进行货架参数化,我们需要在交互界面上进行参数调整,右键打开RackLane,打开Dialog,在Rack_tab的tab下创建相应的对象,并创建NumberOfRightRack和NumberOfLeftRack作为数据连接对象。通过Rack._3D.VisibleGraphicGroups := ["default"]来切换需要显示的Rack,当不需要显示则可以Rack._3D.VisibleGraphicGroups := [""]即可。首先,加载默认的HBW库到模型中,确保结构树当中有。

santayana的专栏 1257

【golang学习之旅】Go 语言并发编程(goroutine、channel 的使用)

✅ 适合人群:Golang 开发者 | 并发编程初学者 | 后端开发工程师✅ 文章亮点:深入解析 Goroutine & Channel + 代码示例 + 实战场景✅ 目标:掌握 Go 并发编程核心,让你的代码更高效、更强大!Goroutine是Go语言的轻量级线程,由Go运行时管理,而非操作系统。相比传统线程,Goroutine占用内存小:默认仅2KB栈空间,可动态扩展调度高效:Go运行时管理Goroutine的调度,无需手动创建线程启动成本低:创建Goroutine比创建线程的开销小很多。

程序员林北北的博客 1252

AIGC游戏开发:如何评估生成内容的玩家满意度?

如何量化评估生成内容对玩家体验的影响?本文聚焦AIGC生成的游戏内容(包括但不限于剧情、关卡、NPC对话、道具),系统探讨玩家满意度的评估方法论,覆盖主观与客观评估、静态与动态内容、短期与长期体验等场景。核心概念:定义AIGC与玩家满意度的关联维度;评估方法:主观(问卷/访谈)与客观(行为/生理数据)评估的技术细节;数学模型:构建多指标融合的评估模型;实战案例:以AIGC生成关卡为例,演示从数据采集到模型落地的全流程;工具资源:推荐数据采集、分析与建模的工具链;

AI天才研究院 1224

Go 语言(Golang)并发编程通俗易懂案例

Go 语言(Golang)天生支持并发编程,它的并发模型基于轻量级的 goroutine 和通信顺序进程(CSP)的思想。Goroutine 是 Go 语言中的并发执行单元,通过通道(channel)来实现不同 goroutine 之间的通信。

weixin_43425950的博客 880

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

14 go语言(golang) - 并发编程goroutine和channel

并发编程是指在计算机程序中同时执行多个计算任务的技术。这种编程方式旨在利用多核处理器的计算能力,提高程序的执行效率和响应速度。而Go 语言的并发编程主要依赖于两个核心概念:Goroutine 和 Channel。

瑞瑞的博客 1076

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

golang笔记10--go语言并发编程模块 goroutine

golang笔记10--go语言的并发编程模块 goroutine1 介绍2 Goroutine2.1 goroutine2.2 go语言的调度器3 注意事项4 说明 1 介绍 本文继上文 golang笔记09–go语言测试与性能调优, 进一步了解 go语言的并发编程模块 --goroutine,以及相应注意事项。 具体包括 : goroutine 和 go语言的调度器 等内容。 2 Goroutine 2.1 goroutine 主流的并发模型包括:多进程、多线程、基于回调的非阻塞|异步IO、协程 等方式

脚步不能达到的地方,眼光可以达到;眼光不能达到的地方,精神可以飞到 474

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【go语言】并发编程

是一个用于描述程序中操作之间因果顺序的规则。在并发编程中,happen-before 机制定义了如何确保一个操作在另一个操作之前发生,并且保证一个操作的结果能够对其他操作可见。具体而言,程序顺序规则(Program Order Rule):在同一个 goroutine 内,代码的执行顺序是保证的,即一个操作发生在前一个操作之后。同步规则(Synchronization Rule):一个 goroutine 对 channel 的发送操作(ch <- x。

加油,考上985的研究生 1471

golang笔记11--go语言并发编程模块 channel

golang笔记11--go语言并发编程模块 channel1 介绍2 Channel2.1 channel2.2 使用Channel等待任务结束2.3 使用Channel进行树的遍历2.4 用select进行调度2.5 传统同步机制3 注意事项4 说明 1 介绍 本文继上文 golang笔记10–go语言并发编程模块 goroutine, 进一步了解 go语言的并发编程模块 --channel,以及相应注意事项。 具体包括 : channel、使用Channel等待任务结束、使用Channel进行树的遍历

脚步不能达到的地方,眼光可以达到;眼光不能达到的地方,精神可以飞到 542

16 go语言(golang) - 并发编程select和workerpool

首先,定义一个WorkerPool最大工作者数(maxWorkers:控制同时运行的 goroutine 的最大数量。任务队列(taskQueue:用于存储待处理任务,通常使用channel来实现。停止信号(stopSignal:一个通道,用于发送停止信号给所有工作者,让它们停止执行。同步机制:如互斥锁(sync.Mutex)或 WaitGroup(),用于同步和等待所有工作者完成。// 池stopSignal chan int // 停止信号,接受到数据时时停止// 保证停止后不能再提交任务。

瑞瑞的博客 1287
上一篇: 9. Palindrome Number回文数(Easy,LeetCode)
下一篇: Go语言的网络编程
DXB2021
博客等级 码龄5年 307粉丝 289原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值