golang搭建项目教程
golang与Java的不同:go的特性体现在通道和高并发,可做中间件,云计算,而java是业务性语言,搭建微服务架构。
项目下的vendor目录
GOROOT/src
GOPATH/src
依赖包加到本地:
go get github.com/go-sql-driver/mysql
govendor:go get只能算下载器而已,govendor提供下载,更新,移除、添加等多种管理命令。
go get -u github.com/kardianos/govendor
cd go_project/src/hello_world
govendor init
govendor fetch github.com/go-sql-driver/mysql
go get github.com/garyburd/redigo/redis
go get -u github.com/confluentinc/confluent-kafka-go/kafka
git clone https://github.com/nsqio/nsq.git;cd nsq;gpm install
这样我们就能在vendor下找到mysql,也能在项目中导入了。
协程是单线程下的并发,又称微线程,纤程
协程是一种用户态的轻量级线程,即协程是由用户程序自己控制调度的
协程能够实现在一条线程上的多个任务互相切换
为了提高工作效率,用户可以控制在一个任务中遇到io就切换
协程由于是在用户态来完成上下文切换的,所以切换耗时只有区区100ns多一些,比进程切换要高30倍。单个协程需要的栈内存也足够小,只需要2KB。
协程底层实现原理
线程是操作系统的内核对象,多线程编程时,如果线程数过多,就会导致频繁的上下文切换(是指CPU 从一个进程或线程切换到另一个进程或线程。
),这些 cpu 时间是一个额外的耗费。所以在一些高并发的网络服务器编程中,使用一个线程服务一个 socket 连接是很不明智的。于是操作系统提供了基于事件模式的异步编程模型。用少量的线程来服务大量的网络连接和I/O操作。但是采用异步和基于事件的编程模型,复杂化了程序代码的编写,非常容易出错。因为线程穿插,也提高排查错误的难度。
协程,是在应用层模拟的线程,他避免了上下文切换的额外耗费,兼顾了多线程的优点。简化了高并发程序的复杂度。举个例子,一个高并发的网络服务器,每一个socket连接进来,服务器用一个协程来对他进行服务。代码非常清晰。而且兼顾了性能。
他和线程的原理是一样的,当 a线程 切换到 b线程 的时候,需要将 a线程 的相关执行进度压入栈,然后将 b线程 的执行进度出栈,进入 b线程 的执行序列。协程只不过是在 应用层 实现这一点。但是,协程并不是由操作系统调度的,而且应用程序也没有能力和权限执行 cpu 调度。怎么解决这个问题?
答案是,协程是基于线程的。内部实现上,维护了一组数据结构和 n 个线程,真正的执行还是线程,协程执行的代码被扔进一个待执行队列中,由这 n 个线程从队列中拉出来执行。这就解决了协程的执行问题。那么协程是怎么切换的呢?答案是:golang 对各种 io函数 进行了封装,这些封装的函数提供给应用程序使用,而其内部调用了操作系统的异步 io函数,当这些异步函数返回 busy 或 bloking 时,golang 利用这个时机将现有的执行序列压栈,让线程去拉另外一个协程的代码来执行,基本原理就是这样,利用并封装了操作系统的异步函数。包括 linux 的 epoll、select 和 windows 的 iocp、event 等。
func Add(x, y int) {
z := x + y
fmt.Println(z)
}
func main() {
for i:=0; i<10; i++ {
go Add(i, i)
}
}
执行上面的代码,会发现屏幕什么也没打印出来,程序就退出了。
对于上面的例子,main()函数启动了10个goroutine,然后返回,这时程序就退出了,而被启动的执行 Add() 的 goroutine 没来得及执行。我们想要让 main() 函数等待所有 goroutine 退出后再返回,但如何知道 goroutine 都退出了呢?这就引出了多个goroutine之间通信的问题。
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
}
}
在这个例子中,定义了一个包含10个channel的数组,并把数组中的每个channel分配给10个不同的goroutine。在每个goroutine完成后,向goroutine写入一个数据,在这个channel被读取前,这个操作是阻塞的。在所有的goroutine启动完成后,依次从10个channel中读取数据,在对应的channel写入数据前,这个操作也是阻塞的。这样,就用channel实现了类似锁的功能,并保证了所有goroutine完成后main()才返回。
go实例
上一篇: Golang如何使用websocket