昨天晚上回家之前还有一个go聊天的聊天系统没有写完,今天接着干完,对了这些天都是看的http://www.topgoer.com/这个教程在学习,这个文档比官方文档写的都要nice,安利一下。
最后写出来的效果,
但是发消息那个消息好像显示不上去,不过和我前不久刚用websocket+netty写的一个手机app的项目有点相似,但是这个go语言的调式那些我还是不太会,因为他运行的时候是 go run server.go …就是几个go文件都一起运行了,不是很了解这样操作的话那我的日志输出在什么地方,怎么去调式他,但是关系不大,因为我看到后面马上就是框架的东西了。
并发介绍
线程和进程
1.进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个单位;
2.线程是进程的一个执行实体,是cpu调度和分派的基本单位,是比进程更小的能独立运行的单位
3.一个进程中可以创建和销毁多个线程,多个线程可以在一个进程中并发运行
并发和并行
多线程程序在一个核的cpu上运行是并发
多线程程序在多个核的cpu上运行是并行
协程和线程
协程:独立栈空间,共享堆空间,调度是用自己完成的,有点类似用户级的线程,只不过这些线程的调度也是由自己完成的
线程:一个线程可以跑多个协程,协程是轻量级的线程
Goroutine
每个实例4~5kb的栈内存占用和由于实现机制而大幅度减少的创建和销毁开销是go实现高并发的根本。
Goroutine 奉行通过共享通信来共享内存,而不是通过共享内存来共享通信。
高并发不是高并行!!!
接着来看下神奇的goroutine
在go中如果要新开启一个线程执行一个函数,只需要在函数前面加上go关键字,但是当主线程结束之后,在这个线程中开启的其他并发线程也会同时结束,主线程就是夜王,其他的就是异鬼哈哈,这个比喻很形象,但是可以加上time.sleep(time.Second),等待其他的线程。
开启多线程的时候,由于线程的资源调度是随机的,所以多线程的执行顺序也是不定的。
有个例子,
我试了一下几种情况
1.主线程注释掉time
2.其他线程注释掉time
其他线程疯狂执行
3.都注释掉
这个意思就是因为开启其他线程是需要时间的,这个时间显然比主线程执行到i2这个时间要长,如果把i2的条件加大可能可以输出 new gouroutine的信息 于是我把条件变成i==1000000,猜想正确
4.都使用time
都使用time,我试了多次运行,发现由两种结果,但是都是主线程结束了,其他的也就结束了
GPM go语言的调度系统
G goroutine
P 是存储了当前goroutine的上下文数据(堆栈地址、地址边界、函数指针)管理着一组goroutine队列,负责调度分配,合理化(时间长的先暂停,)如果自己的消费完了去全局搜索,全局完了,去其他队列抢任务
M machine 最终的goroutine都是运行在这个上面的,于内核对应
总结,go单从线程角度来讲比起其他的编程语言的优势在于os线程由os系统调度,自己的goroutine是用自己的GPM完成的调度,调度是有个M:N的调度技术调度M个goroutine到n个os线程,具体的就不太了解了。
Channel通道
1.全局定义 var chan ch1
2.ch1 := make(chan int)
3.发送 ch1 <-10
4.接受 i := <- ch1
5.close()
6.有缓存的通道定义 make(chan int ,10)
7.遍历通道中的数据
方法1:
方法2:
8.单向通道 通道作为参数传递的时候,可以定义通道的流向
chan<- int是一个只能发送的通道,可以发送但是不能接收;
<-chan int是一个只能接收的通道,可以接收但是不能发送。
9.通道异常总结
定时器
使用的时候再去查
Ticker 可以多次使用的
Select 用法和swicth相同
可以用于响应多个通道的数据情况,可以监听多个channel直到其中有一个already,如果同时出现,会随机选择一个执行
并发安全锁
在启用多个goroutine去访问共享资源的时候,会因为临界区的问题出现数据的争夺,导致结果出错。
互斥锁:在数据访问之前先锁定,确保其他线程处于等待状态,操作完再解锁,其他线程操作的时候同样进行这样的操作
读写互斥锁:用于读操作多于写操作的时候
Lock mutex rwLock RWMUTEX
Sync
WaitGroup
其内部维护着一个计数器,一共有三个方法
Add() 计数器增加 Done() 计数器-1 wait()等待计数器为0
Once
用来做数据初始话,其内部使用互斥锁维护着一个布尔值,这个布尔值记录初始化是否完成,在初始化的时候,其他线程无法操作。
用法:
loadIconsOnce.Do(loadIcons)
代替原来的写法
if icons == nil {
loadIcons()
}
Map
线程安全的map,go语言内置的map是并发不安全的,
原子操作
Go对于基本数据类型的操作提供了一种叫做原子操作的东西,相比加锁的性能消耗而言,使用原子操作的性能要优于加锁
GPM调度器的生命周期
图片是从教程上面截取的就没有再去画了。
1.创建第一个线程MO
2.创建第一个Go协程GO
3.关联MO与GO
4.调度初始化
5.创建main()中的goroutine
6.启动MO
7.M绑定P
8.判断M是否能通过P获取到G,如果不能M休眠,直至再猜被唤醒,继续跳到7
如果可以获取到,继续
9.M设置G环境
10.M执行G
11.G执行完成之后,跳到8
接着就是一个比较高深的线程上面的问题了,但是我现在感觉自己看这个花费的时间会比较多,于是我就看那个我一直想看的操作数据的了。
数据库操作
但是注意的是struct中如果的变量名,名称的首字母一定要用大写的,不然会报一个错
non-struct dest type struct with >1 columns (4)
如果数据库不是varchar类型的时候,比如是decimal接受的可以用int或者float
增删改就不写了
但是在实例中有一段这个,上面的写法会说数据库已经关闭了,我觉得应该是位置写错了,我把他移到了main函数中,试一下。
猜想正确,因为在调用的地方加上才可以。这里还有个没有搞懂就是那个init方法,我在main方法中都没有调用,但是他却自己就给执行了。反正记着之后遇到这种先就记到吧,后面应该有地方会解释的。
Redis操作
|
请发表评论