2021/10/04 老男孩带你21周搞定Go语言 (七)

P103今日内容

在这里插入图片描述

P104 内容回顾

**在调用前面加上go关键字,就可以开启一个goroutine去执行函数。
goroutine对应函数执行完,该goroutine就结束了。
main函数启动会自动创建一个goroutine去执行main函数/。
**
在这里插入图片描述
sync.WaitGroup等待组,启动多个goroutine,也不知道哪个结束,要等待goroutine结束,就需要阻塞,之前使用sleep,但sleep不知道goroutine具体什么时候结束。
等待组有三个使用方法。
定义全局变量var wg sync.WaitGroup,申明一个结构体。
开启一个goroutine干活都需要登记一下,wg.Add(1)
工人结束后,要在goroutine内部执行,wg.Done(),告诉外面干完活了。计数器减一。
在干活的时候需要等,wg.Wait()
在主的main函数里等待其他goroutine是很常见的。

在这里插入图片描述

goroutine调度模型是GMP
在这里插入图片描述
goroutine和操作系统OS线程的区别。
一个进程里至少要有一个线程,线程是负责干活的。
goroutine是用户态的线程,操作系统是内核态的, 比内核态的线程更加轻量一点。开启一个操作系统线程至少要2M空间,开启一个goroutine只需要2KB,而且可以动态变化,最大扩展到1G。
可以轻松开启数十万个goroutine。
go的调度,其实是把M个goroutine调度到n个线程上干活。

在这里插入图片描述

在这里插入图片描述
goroutine设置运行时占用多少核,runtime.GOMAXPROCS,go1.5之后就是操作系统的逻辑核心数 。默认跑满cpu,在跑记日志 的时候可以设定占用资源小一点。
在这里插入图片描述
n就是起多个goroutine干活,for循环里,就是把这段代码执行三遍
在这里插入图片描述

work pool模式。动态调整资源占用,有三个goroutine,但是有5个任务。
在这里插入图片描述
为什么需要channel?
通过channel实现多个goroutine之间的通信。
CSP:通过通信来共享内存
channel是引用类型,make函数初始化之后才能使用。(slice,map,channel)
channel的声明:var ch chan 元素类型
初始化: ch = make(chan 元素类型,【缓冲区大小】
操作:
发送 ch <- 100
接收 x := <- ch
关闭 close(ch_

在这里插入图片描述
带缓冲区和不带缓冲区的区别,满了的话有人接才能放

在这里插入图片描述

需要从循环里去读取

在这里插入图片描述
通道里取完,应该返回个false,现在模拟一下,出现死锁,说明程序有问题。
在这里插入图片描述
在这里插入图片描述
缓冲区只有1个,存两个,就没有位置了,也需要等待。同样,只有1个取2个的时候,也要等
在这里插入图片描述
可以有其他的服务往channel传值
在这里插入图片描述
取值也循环去取
在这里插入图片描述
中间的停顿是阻塞在那里等
在这里插入图片描述
通道满了,再放进去就会阻塞,通道没了,再取就会阻塞,所以通道close掉,再取值就会返回false。

在这里插入图片描述
通道空了,再去接收值就是阻塞。满了,再发送值,也是阻塞。
通道里有值,但是关闭了,还能取值。取完返回对应0值和false。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

select,一般考虑同一时刻有多个通道的时候使用,多路复用。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
128个goroutine遍历500给任务,,循环从result取值,但是程序永远卡死,因为一直再取,但是通道关闭了

在这里插入图片描述
想要改造成取完就退出。死锁是因为results没有关闭,把所有计算结果放到results之后就可以关闭,就不会死锁了

在这里插入图片描述
for循环只是启动128个goroutine,具体完成不完成是不知道的
在这里插入图片描述
··
个数少点可以看到多少个

在这里插入图片描述
在这里插入图片描述
等的是goroutine结束,但是打印还来不及打印

在这里插入图片描述
匿名结构体不占空间
在这里插入图片描述
worker,做完往通道里发送值,现在不用cat,使用了一个匿名结构体。
在这里插入图片描述
使用了一个匿名结构体。
在这里插入图片描述
再写一个匿名函数,从notify取值,取5次,close掉通道
在这里插入图片描述
在这里插入图片描述
**先开启5个任务,5个任务相当于5个for循环,放进去任务,关闭jobs通道。
然后开启3个goroutine,这三个goroutine,从jobs取值,计算放到results里。
notify就发送信号,这个信号告诉已经执行5次。
**

在这里插入图片描述
5次在这里执行goroutine,专门从notifych里取5次值。5次后关闭results

在这里插入图片描述
这里就执行退出

在这里插入图片描述
在这里插入图片描述

P105 几个作业的问题

切完日志,往关闭的日志写就报错

在这里插入图片描述
首字母大写,但是返回值又是小写,是矛盾的,就不能对外暴露

在这里插入图片描述
切割的时候,第一条日志往心的文件里写,其他的还是往旧的写,旧的文件已经关闭,会报错

在这里插入图片描述
这一步没有生效
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
往里面写日志

在这里插入图片描述
现在就已经切换了

在这里插入图片描述
出现问题, 是因为指针的问题
在这里插入图片描述
这种传进来就是一个拷贝

在这里插入图片描述
三个点代表可变参数,传0个或是多个

在这里插入图片描述
是一个slice切片

在这里插入图片描述

在这里插入图片描述
多个值就是int类型的切片
在这里插入图片描述
空接口,空接口的切片接收任何值
在这里插入图片描述
当作一个整体传到f1里去了
在这里插入图片描述
空接口 切片第一个元素是int类型切片
在这里插入图片描述
slice嵌套slice,…点点点相当于拆开,一次次传进去

在这里插入图片描述
在这里插入图片描述

P106 异步写日志

之前是同步写,一条写成功后 ,再往下写

在这里插入图片描述

可以把日志作为一个结构体存到通道里
在这里插入图片描述
初始化

在这里插入图片描述
现在要 把生成的日志往通道里面 写

在这里插入图片描述
如果日志enable,就往通道里写,根据结构体造一个logmsg对象

在这里插入图片描述
造完对象,还需要写到通道里,但是如果通道满了,再往里放,就阻塞了,程序就不会返回了,代码不会往下走了。
在这里插入图片描述
可以使用select

在这里插入图片描述
在这里插入图片描述
可以先把日志拼出来
在这里插入图片描述
这里加上select,避免阻塞在这里插入图片描述
如果取不出来,default可以先sleep一会
在这里插入图片描述
可以开启5个goroutine去写
在这里插入图片描述
现在就成了异步写日志

在这里插入图片描述
先构造一个通道(类似如果是字符串,就太大了,可以使用结构体。)
在这里插入图片描述
创建日志 实例的时候,要进行通道初始化,初始化后才能使用

在这里插入图片描述
在这里插入图片描述
设定大小变量,这样修改的时候,不会在代码里写了
在这里插入图片描述
开启一个线程应该就不会有问题
在这里插入图片描述
这里针对多个线程可能不行,并发的时候,切换的时候,有可能其他还在写日志

在这里插入图片描述
在这里插入图片描述
多个goroutine往同一个文件写多少有点问题

在这里插入图片描述
在这里插入图片描述

P107 互斥锁

在这里插入图片描述
某些goroutine启动要去访问全局变量,add函数循环加5000次,go起了2个add
在这里插入图片描述
先试试下面的,按顺序走完

在这里插入图片描述

在这里插入图片描述
开启两个goroutine去做这个事情,现在每次的值都不一样。
在这里插入图片描述
x是先去公共区域,拿到x,在这个基础上+1 ,再送回去。这个动作,两个goroutine都在做,都在自己区域里+1,往外放。正常是1个1个放,现在都是单独加1,比如两个50+1 ,其实是50+1+1,但是返回的时候变成加1个,无形中就少了。
一个人锁了之后,其他人需要等锁释放了才能使用。
互斥锁能保证同一时间只有一个goroutine去访问共享资源。

在这里插入图片描述
在这里插入图片描述
锁本身是个结构体变量
在这里插入图片描述
对一个公共资源操作的时候要加锁
在这里插入图片描述
现在就恢复正常了

在这里插入图片描述
在这里插入图片描述

P108 读写互斥锁

一个goroutine获取读锁之后,其他的读锁也会继续获得锁,如果是获取写锁,其他的groutine才能去读和写,写锁释放了,其他才能去读去写。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
进行时间的加减
在这里插入图片描述
试试互斥锁,读完之后 解锁
在这里插入图片描述
写锁
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
go一次,wg.Add()加一个
在这里插入图片描述
都出来都是正确的
在这里插入图片描述
写10次,读1000次
在这里插入图片描述
2秒多

在这里插入图片描述
下面是读写互斥锁,级别高一点。
在这里插入图片描述
读写锁,读操作+R

在这里插入图片描述
读锁
在这里插入图片描述
写锁
在这里插入图片描述
速度很快,但是每次没加完的时候就已经在读了

在这里插入图片描述
sleep,先让加再跑起来
在这里插入图片描述
在这里插入图片描述
换回写锁,互斥锁
在这里插入图片描述
在这里插入图片描述
**当读操作大于写操作的时候,互斥锁,1000次读就不需要让别人等 **

在这里插入图片描述

P109 sync.Once示例

在sync某些包里,针对某些特定的场景,执行某些操作,只需要执行一次就可以了。

在这里插入图片描述
icons是个map,可以程序一启动就加载,也可以使用的时候才加载。但是这样并发是不安全的,因为loadIcons是用的全局变量。多个goroutine都去做初始化就不安全了。
在这里插入图片描述
现在的cpu可能保证每个goroutine都是去串行的
在这里插入图片描述

确保某个函数或操作只执行一次,sync.Once里也是个结构体,一个锁,加上一个标志位,第一次做的时候判断标志位是否为true,true代表已经做过,就往下走,false代表没有执行过,就先加锁再执行,执行完就释放锁。
在这里插入图片描述
通道数只能关闭一次

在这里插入图片描述
在这里插入图片描述
Once是确保通道数只关闭一次
在这里插入图片描述
执行了多个goroutine,如果都要关闭,肯定就报错,

在这里插入图片描述
这个函数要求没有参数没有返回值
在这里插入图片描述
就可以做一个闭包
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
先加锁,defer确保最后释放锁,置为1后,后面的goroutine就直接跳过了在这里插入图片描述
写一个闭包函数满足once方法传递的一个无返回值函数,变量会去上面找。
在这里插入图片描述

P110 sync.Map示例

做配置文件的时候,一般都需要map类型来存储,go内置的map不是并发安全的,多个goroutine访问会出现问题。

在这里插入图片描述
在这里插入图片描述
起20个goroutine,每次起一个设置一个值,返回一个值
在这里插入图片描述
在这里插入图片描述
并发写map就报错,并发超过20的时候编译器就会报错
在这里插入图片描述

在这里插入图片描述
加锁
在这里插入图片描述
加了锁,试试超过21个goroutine
在这里插入图片描述
在这里插入图片描述
考虑到这个问题,go提供了并发安全版的map
在这里插入图片描述
set可以使用提供的方法Store,存储,必须使用store存值,开箱即用,不用初始化。
在这里插入图片描述

P111 atomic原子性操作

atomic原子
在这里插入图片描述
之前并发修改全局的是会有问题的,就需要加锁

在这里插入图片描述
atomic这个包提供的一个addint64对于 int64的累加操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

每一次都不一样,10万个goroutine肯定去读这个全局变量,会并发不安全
在这里插入图片描述
一种原始方式是加全局锁
在这里插入图片描述
用原子包里提供的方法对x做加一操作。
在这里插入图片描述
在这里插入图片描述
load是全局安全读取值,store全局存值,add全局修改

在这里插入图片描述
swap全局交换,compare全局比较并交换操作。
在这里插入图片描述
在这里插入图片描述
如果值相当0,设置成100
在这里插入图片描述
false还是原来的值

在这里插入图片描述

P112互联网协议介绍

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
数据链路层,有帧的概念,以太网的概念,广播arp
在这里插入图片描述
ip地址是网络层
在这里插入图片描述
tcp和udp
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

P113 TCP服务端客户端开发

在这里插入图片描述
socket属于一个抽象层,用户写程序不用关心其他的协议方法,只关注调用socket规定的相关函数就行。socket把下面都屏蔽掉了,不需要关心下面的

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

写一个客户端
在这里插入图片描述
在这里插入图片描述
把值传进去

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
scanln遇到空格就结束了,还需要改一下,使用bufio
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
另外再开一个客户端
在这里插入图片描述
服务端也需要for循环,现在这样是一个人收一次,其他人收不到了

在这里插入图片描述
改成for循环

在这里插入图片描述
在这里插入图片描述
可以写一个回复
在这里插入图片描述
在这里插入图片描述

P114 解决粘包的问题

tcp是基于流式的数据,没有明确的开始和结束,这次结束和下次要发的一起发给你了,这就是tcp年粘包。
nagle算法提交一段消息给tcp的时候,tcp并不立刻发送这条数据,等待是否有其他的包,如果有其他的包,一起发送。副作用就是出现粘包。
会造成tcp缓冲区中存放了几段数据。

在这里插入图片描述

在这里插入图片描述
创建一个读,读1024个字节。
在这里插入图片描述
本地监听30000端口

在这里插入图片描述
客户端

在这里插入图片描述
在这里插入图片描述
并不是我们想的一次发一句,因为nagle算法,不满包,会等待一段时间,如果有数据。把后面的数据放在一起
在这里插入图片描述
等待一下就是,每次运行一次
在这里插入图片描述

在这里插入图片描述
可以规定自己前面发的包的4个字节代表大小,就可以去里面判断大小,每次读多少
在这里插入图片描述
在这里插入图片描述
编码一个方法,解码一个方法
在这里插入图片描述
编码方法把消息大小算出来
在这里插入图片描述
高位可以写在左边也可以写在右边,从内存里读数据,从左开始读还是右开始,就需要告诉它
在这里插入图片描述
在这里插入图片描述
这是小端的方式,往包里写长度

在这里插入图片描述
包大小解出来,读指定大小的数据。

在这里插入图片描述
在这里插入图片描述
发消息的时候写
在这里插入图片描述
server端收消息,从编码里取需要读多少大小的数据
在这里插入图片描述
在这里插入图片描述
这样数据就发送过来了

在这里插入图片描述
在这里插入图片描述
这样每次收集的数据都是固定大小的数据

在这里插入图片描述

P115 UDP客户端服务端

UDP属于一个不可靠吗,没有时序的协议
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不需要建立链接直接通信
在这里插入图片描述
在这里插入图片描述
客户端
在这里插入图片描述
在这里插入图片描述
客户端从中间就可以收发信息,无限循环,bufio造一个读对象
在这里插入图片描述
收到转换大写
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值