go语言基础之并发和网络

wjq310 wjq310     2022-08-24     641

关键词:

1、goroutine
在这章中将展示 Go 使用 channel 和 goroutine 开发并行程序的能力。
goroutine 是 Go 并发能力的核心要素。但是,goroutine 到底 是什么?
叫做 goroutine 是因为已有的短语——线程、协程、进程等等——传 递了不准确的含义。
goroutine 有简单的模型:它是与其他 goroutine 并行执行的,
有着相同地址空间的函数。。它是轻量的,仅比分配 栈空间多一点点 。
而初始时栈是很小的,所以它们也是廉价的,并且随着需要在堆空间上分配(和释放)。
 
goroutine 是一个普通的函数,只是需要使用保留字 go 作为开头。
ready("Tea", 2) ← 普通函数调用
go ready("Tea", 2) ← ready() 作为 goroutine 运行
 
Go routine 实践
func ready(w string, sec int) {
time.Sleep(time.Duration(sec) * time.Second)
fmt.Println(w, "is ready!")
}
func main() {
go ready("Tea", 2)
go ready("Coffee", 1)
fmt.Println("I‘m waiting")
time.Sleep(5 * time.Second)
}
输出:
I‘m waiting ← 立刻
Coffee is ready! ← 1 秒后
Tea is ready! ← 2 秒后
 
2、用 channel
如果不等待 goroutine 的执行(例如,移除 I’m waiting 行),
程序立刻终止,而任何正在执行的 goroutine 都会停止。
为了修复这个,需要一些能够同 goroutine 通讯的机制。
这一机制通过 channels 的形式使用。
channel 可以与 Unix sehll 中的双向管道做类比:可以通过它发送或者接收值。
这些值只能是特定的类型:channel 类型。定义一个 channel 时,
也需要定义发送到 channel 的值的类型。
注意,必须使用 make 创建 channel:
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
创建 channel ci 用于发送和接收整数,创建 channel cs 用于字符串,
以及 channel cf 使用了空接口来满足各种类型。
向 channel 发送或接收数据,是通过类似的 操作符完成的:<-.
具体作用则依赖于操作符的位置:
ci <- 1 ←发送整数1 到channelci
<-ci ← 从 channel ci 接收整数
i := <-ci ← 从 channel ci 接收整数,并保存到 i 中
 
将这些放到实例中去:
 
// 定义 c 作为 int 型的 channel。就是说:这个 channel 传输整数。
// 注意这个变量是全局的,这样 goroutine 可以访问它;
var c chan int
func ready(w string, sec int) {
time.Sleep(time.Duration(sec) * time.Second)
fmt.Println(w, "is ready!")
c <- 1 // 发送整数1 到 channel c;
}
func main() {
c = make(chan int) // 初始化c
go ready("Tea",2) // 用保留字go 开始一个 goroutine
go ready("Coffee", 1)
fmt.Println("I‘m waiting, but not too long")
<-c // 等待,直到从 channel 上接收一个值。注意,收到的值被丢弃了;
<-c // 两个 goroutines,接收两个值。
}
 
如果不知道有启动了多少个 goroutine 怎么办呢?
这里有另一个 Go 内建的保留字:select。
通过 select(和其他东西)可以监听 channel 上输入的数据。
 
在这个程序中使用 select,并不会让它变得更短,因为运行的 goroutine 太少 了。
移除两行 <-c,并用下面的内容替换它们:
使用 select
L: for {
select {
case <-c:
i++
if i > 1 {
break L
}
}
}
现在将会一直等待下去。
只有当从 channel c 上收到多个响应时才会退出循环 L。
 
3、并行运行
虽然 goroutine 是并发执行的,但是它们并不是并行运行的。
如果不告诉 Go 额 外的东西,同一时刻只会有一个 goroutine 执行。
利用 runtime.GOMAXPROCS(n) 可以设置 goroutine 并行执行的数量。
 
GOMAXPROCS 设置了同时运行的 CPU 的最大数量,并返回之前的设 置。
如果 n < 1,不会改变当前设置。 当调度得到改进后,这将被移 除。
 
当在 Go 中用 ch := make(chan bool) 创建 chennel 时,bool 型的 无缓冲 channel 会被创建。
这对于程序来说意味着什么呢?首先,如果读取(value := <-ch)它 将会被阻塞,直到有数据接收。
其次,任何发送(ch<-5)将会被阻塞,直到数 据被读出。
无缓冲 channel 是在多个 goroutine 之间同步很棒的工具。
不过 Go 也允许指定 channel 的缓冲大小,很简单,就是 channel 可以存储多少 元素。
ch := make(chan bool, 4),创建了可以存储 4 个元素的 bool 型 channel。
在这个 channel 中,前 4 个元素可以无阻塞的写入。
当写入第 5 元素时,代码 将会阻塞,直到其他 goroutine 从 channel 中读取一些元素,腾出空间。
 
 
4、关闭 channel
当 channel 被关闭后,读取端需要知道这个事情。下面的代码演示了如何检查
channel 是否被关系。
x, ok = <-ch
当 ok 被赋值为 true 意味着 channel 尚未被关闭,同时 可以读取数据。
否则 ok 被 赋值为 false。在这个情况下表示 channel 被关闭。
 
 
十七、通讯
了解文件、目录、网络通 讯和运行其他程序。
Go 的 I/O 核心是接口 io.Reader 和 io.Writer。
在 Go 中,从文件读取(或写入)是非常容易的,即使用os包
 
1、从文件读取(无缓冲)
package main
import "os"
func main() {
buf := make([]byte, 1024)
f,_ := os.Open("/etc/passwd") // 打开文件,os.Open 返回一个实现了 io.Reader 和 io.Writer 的 *os.File;
defer f.Close() // 确保关闭了f;
for {
n,_ := f.Read(buf) // 一次读取1024 字节
if n==0 {break} // 到达文件末尾
os.Stdout.Write(buf[:n]) // 将内容写入os.Stdout
}
}
 
 
2、从文件读取(缓冲,用bufio包)
package main
import ( "os"; "bufio")
func main() {
buf := make([]byte, 1024)
f,_ := os.Open("/etc/passwd") // 打开文件
defer f.Close()
// 转换 f 为有缓冲的 Reader。NewReader 需要一个 io.Reader,
// 因此或许你认为这会出错。但其实不会。任何有 Read() 函数就实现了这个接口。
// 同时, 从上例可以看到,*os.File 已经这样做了;
r := bufio.NewReader(f)
w := bufio.NewWriter(os.Stdout)
defer w.Flush()
for {
n, _ := r.Read(buf) // 从 Reader 读取,而向 Writer 写入,然后向屏幕输出文件。
if n == 0 { break }
w.Write(buf[0:n])
}
}
 
3、io.Reader
io.Reader 接口对于 Go 语言来说非常重要。
许多(如果不是全 部的话)函数需要通过 io.Reader 读取一些数据作为输入
 
4、一行一行读取文件
f,_ := os.Open("/etc/passwd");
defer f.Close()
r := bufio.NewReader(f) ← 使其成为一个 bufio,以便访问 ReadString 方法
s, ok := r.ReadString(‘ ‘) { ← 从输入中读取一行
// ... | ← s 保存了字符串,通过 string 包就可以解析它|
 
 
5、命令行参数
一个 DNS 查询工 具:
// 定义 bool 标识,-dnssec。变量必须是指针,否则 package 无法设置其值
dnssec := flag.Bool("dnssec",false,"RequestDNSSECrecords")
port := flag.String("port", "53", "Set the query port") // 类似的,port 选项;
flag.Usage = func() { // 简单的重定义 Usage 函数
fmt.Fprintf(os.Stderr, "Usage: %s [OPTIONS] [name ...] ", os.Args[0])
flag.PrintDefaults() // 指定的每个标识,PrintDefaults 将输出帮助信息;
}
flag.Parse() // 解析标识,并填充变量。
当参数被解析之后,就可以使用它们:
if *dnssec { ← 定义传入参数 dnssec
// 做点
}
 
6、执行命令
os/exec 包有函数可以执行外部命令,这也是在 Go 中主要的执行命令的方法。
通过定义一个有着数个方法的 *exec.Cmd 结构来使用。
执行 ls -l:
import "os/exec"
cmd := exec.Command("/bin/ls", "-l")
err := cmd.Run()
上面的例子运行了 “ls -l”,但是没有对其返回的数据进行任何处理
 
通过如下 方法从命令行的标准输出中获得信息:
import "exec"
cmd := exec.Command("/bin/ls", "-l")
buf, err := cmd.Output() ← buf 是一个 []byte
 
 
7、网络
所有网络相关的类型和函数可以在 net 包中找到。这其中最重要的函数是 Dial。
当 Dial 到远程系统,这个函数返回 Conn 接口类型,可以用于发送或接收信息。
函数 Dial 简洁的抽象了网络层和传输层。
因此 IPv4 或者 IPv6,TCP 或者 UDP 可 以共用一个接口。
 
通过 TCP 连接到远程系统(端口 80),然后是 UDP,最后是 TCP 通过 IPv6,
大致 是这样:
conn, e := Dial("tcp", "192.0.32.10:80")
conn, e := Dial("udp", "192.0.32.10:80")
conn, e := Dial("tcp", "[2620:0:2d0:200::10]:80") ← 方括号是强制的
如果没有错误(由 e 返回),就可以使用 conn 从套接字中读写。
在包 net 中的原 始定义是:
// Read reads data from the connection.
Read(b []byte)(n int, err error)
这使得 conn 成为了 io.Reader。
// Write writes data to the connection.
Write(b []byte)(n int, err error)
这同样使得 conn 成为了 io.Writer,
事实上 conn 是 io.ReadWriter。
但是这些都是隐含的低层
 
通常总是应该使用更高层次的包:http
一个简单的 http Get 例子
package main
import("io/ioutil";"net/http";"fmt") // 需要的导入
func main() {
r,err := http.Get("http://www.google.com/robots.txt") // 使用http.Get获取html
if err != nil { fmt.Printf("%s ",err.String()); return } // 错误处理
b, err := ioutil.ReadAll(r.Body) // 将整个内容读入b
r.Body.Close()
if err == nil { fmt.Printf("%s",string(b)) } // 如果一切ok,打印内容
}

go语言基础之并发concurrency

并发Concurrency  很多人都是冲着Go大肆宣扬的高并发而忍不住跃跃欲试,但其实从源码的解析来看,goroutine只是由官方实现的超级“线程池”而已。不过话说回来,每个实例4~5KB的栈内存占用和由于实现机制而大幅减少的创建... 查看详情

go语言之并发(代码片段)

一:并发基础1并发和并行并发和并行是两个不同的概念:1并行意味着程序在任意时刻都是同时运行的:2并发意味着程序在单位时间内是同时运行的详解:  并行就是在任一粒度的时间内都具备同时执行的能力:最简单的并行... 查看详情

go语言基础之并发(代码片段)

并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因。Go语言中的并发编程并发与并行并发:同一时间段内执行多个任务并行:同一时刻执行多个任务Go语言的并发通过goro... 查看详情

19.go语言基础之并发(代码片段)

1.1并发与并行并发:同一时间段执行多个任务(使用微信和多个朋友聊天)并行:同一时刻执行多个任务(windows中360在杀毒,同时你也在写代码)Go语言的并发通过goroutine实现。goroutine类似于线程,属于用户态的线程,我们可以... 查看详情

go语言学习之旅--并发编程

Go语言学习之旅--并发编程golang并发编程之协程golang并发编程之通道golang并发编程之WaitGroup实现同步golang并发编程之runtime包golang并发编程之Mutex互斥锁实现同步golang并发编程之channel的遍历golang并发编程之selectswitchgolang并发编程之T... 查看详情

go语言学习之旅--并发编程

Go语言学习之旅--并发编程golang并发编程之协程golang并发编程之通道golang并发编程之WaitGroup实现同步golang并发编程之runtime包golang并发编程之Mutex互斥锁实现同步golang并发编程之channel的遍历golang并发编程之selectswitchgolang并发编程之T... 查看详情

go语言之并发示例-pool

这篇文章演示使用有缓冲的通道实现一个资源池,这个资源池可以管理在任意多个goroutine之间共享的资源,比如网络连接、数据库连接等,我们在数据库操作的时候,比较常见的就是数据连接池,也可以基于我们实现的资源池来... 查看详情

go语言基础之并发(代码片段)

Go语言中的并发编程——并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因。并发与并行并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天)。并... 查看详情

go语言之路—博客目录

...备从零开始搭建Go语言开发环境VSCode配置Go语言开发环境Go语言基础Go语言基础之变量和常量Go语言基础之基本数据类型Go语言基础之运算符Go语言基础之流程控制Go语言基础之数组Go语言基础之切片Go语言基础之mapGo语言基础之函数Go... 查看详情

融云开发漫谈:你是否了解go语言并发编程的第一要义?

...关书籍也如雨后春笋般涌现,前不久,一本名为《Go语言并发之道》的书籍被翻译引进国内,并迅速引起广泛关注,本书由融云的一位研发工程师赵晨光联合翻译,旨在帮助Go语言学习者了解并发设计的模式和应用场景,更加深... 查看详情

go语言系列之并发编程(代码片段)

Go语言中的并发编程并发与并行并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天)。并行:同一时刻执行多个任务(你和你朋友都在用微信和女朋友聊天)。Go语言的并发通过goroutine实现。goroutine类似于线程,... 查看详情

go语言学习之旅--并发编程

Go语言学习之旅--并发编程golang并发编程之协程golang并发编程之通道golang并发编程之WaitGroup实现同步golang并发编程之runtime包golang并发编程之Mutex互斥锁实现同步golang并发编程之channel的遍历golang并发编程之selectswitchgolang并发编程之T... 查看详情

go基础之程序结构数据类型(代码片段)

...让你访问底层操作系统,还提供了强大的网络编程和并发编程支持。Go语言的用途众多,可以进行网络编程、系统编程、并发编程、分布式编程。Go语言的推出,旨在不损失应用程序性能的情况下降低代码的复杂性ÿ... 查看详情

go基础之程序结构数据类型(代码片段)

...让你访问底层操作系统,还提供了强大的网络编程和并发编程支持。Go语言的用途众多,可以进行网络编程、系统编程、并发编程、分布式编程。Go语言的推出,旨在不损失应用程序性能的情况下降低代码的复杂性ÿ... 查看详情

go基础之文件操作命令行参数序列化并发编程(代码片段)

Go基础(三)之文件操作、命令行参数、序列化、并发编程一、文件操作1.1打开和关闭文件1.2读取文件1.2.1按字节读取:file.Read()1.2.2bufio按行读取文件1.2.3ioutil读取整个文件1.3文件写入1.3.1Write和WriteString1.3.2bufio.NewWriter1... 查看详情

go语言之并发编程channel

单向channel:单向通道可分为发送通道和接收通道。但是无论哪一种单向通道,都不应该出现在变量的声明中,假如初始化了这样一个变量varuselessChanchan<-int=make(chan<-int,10)这样一个变量该如何使用呢,这样一个只进不出的通道... 查看详情

go语言之并发资源竞争

并发本身并不复杂,但是因为有了资源竞争的问题,就使得我们开发出好的并发程序变得复杂起来,因为会引起很多莫名其妙的问题。package mainimport (    "fmt"    "runtime"    "sync")... 查看详情

go语言之并发示例-pool

针对这个资源池管理的一步步都实现了,而且做了详细的讲解,下面就看下整个示例代码,方便理解。package commonimport (    "errors"    "io"    "sync"    "log")//一 查看详情