go高并发之协程的概念以及入门案例
近期将更新有关go高并发的一些知识
将会有三个系列
- go协程(goroutine)
- go管道(channel)
- goroutine + channel 构建高并发程序
这三系列将解决如下问题?
要求统2-90000000000的数字中,那些是素数?
//显然用传统的方法是不可行的
//这时候用到goroutine + go channel 可以有效的解决问题
本文是有关go协程的一些概念以及入门案例
效果如下图
什么是进程与线程?
进程是一个运行在自己内存地址空间里的独立执行体
一个应用程序就是一个进程
在linux中使用top命令即可看到各种进程
可以看到第一个进程就是netease,网易云音乐,有自己独立的一个进程id
一个进程由一个或多个线程组合
这些线程是共享同一个内存地址的一起工作的执行体
比如
当下载网盘中的数据时,可以同时下载几个文件
这就是多线程,以便加快速度
一个进程中包含一个或多个线程
并发与并行的区别
这里涉及到一个问题,很多同学搞不清楚并发与并行的区别,这里我根据我根据知乎上这个问题某位网友的例子,我觉得很好
- 你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行
- 你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发
- 你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行
并发:你有处理多个任务的能力,不一定同时(一个CPU轮流)
并行:有同时处理多个任务的能力(多个CPU同时)
并发和并行都可以是很多个线程,就看这些线程能不能同时被(多个)CPU执行,可以说明是并行,并发是多个线程被一个CPU轮流切换着执行
线程是一个物理线程,直接作用在cpu上的,是重量级的,非常耗费cpu资源。其他编程语言实现并发是通过开启多个线程,这样对资源耗费太大。
go实现并发和并行的方式
在go中,应用程序并发处理的部分被称作goroutines(协程)
它是轻量级的线程
占用的的内存和资源小
go协程的特点
- 有独立的栈空间
- 共享程序堆空间
- 调度由用户控制
- 协程是轻量级的线程
go实现并行
可以指定程序使用的cpu数
实现多核运算,带来性能的提升
这也是go最大的亮点
快速入门案例
首先来一个常规的程序
package main
import "fmt"
func TestHello() {
for i := 0; i < 5; i++ {
fmt.Println("testHello()", "HelloWorld")
//等待一秒
time.Sleep(time.Second)
}
fmt.Println("test() complete!")
}
func main() {
TestHello()
for i := 0; i < 5; i++ {
fmt.Println("main()", "HelloWorld")
//等待一秒
time.Sleep(time.Second)
}
fmt.Println("main() complete!")
}
程序执行的结果
显然是先完成TestHello()的调用
再去输出下面的内容
再看goroutine的案例
仅仅在调用的TestHello前面加了go关键字
即开启一个协程
package main
import (
"fmt"
"time"
)
/*
//快速入门
在主线程(可以理解成进程)中,开启一个goroutine,该协程每隔1秒输出"hello world"
在主线程中也每隔一秒中输出"hello world",输出10次后,退出程序
要求主线程和goroutine同时执行
*/
func TestHello() {
for i := 0; i < 5; i++ {
fmt.Println("testHello()", "HelloWorld")
//等待一秒
time.Sleep(time.Second)
}
fmt.Println("test() complete!")
}
func main() {
//调用TestHello()
go TestHello()
for i := 0; i < 5; i++ {
fmt.Println("main()", "HelloWorld")
//等待一秒
time.Sleep(time.Second)
}
fmt.Println("main() complete!")
}
常规方式的流程图
使用goroutine流程图
再来一个实际的例子
比如现在我要下载两个文件
使用goroutine并发执行
package main
import (
"fmt"
"time"
)
/*
//快速入门
在主线程(可以理解成进程)中,开启一个goroutine,该协程每隔1秒输出"hello world"
在主线程中也每隔一秒中输出"hello world",输出10次后,退出程序
要求主线程和goroutine同时执行
*/
func Download1() {
fmt.Println("开始下载1.avi")
//假设需要5秒的时间
time.Sleep(5 * time.Second)
fmt.Println("complete 1.avi")
}
func Download2() {
fmt.Println("开始下载2.avi")
//假设需要4秒的时间
time.Sleep(4 * time.Second)
fmt.Println("complete 2.avi")
}
func main() {
start := time.Now()
fmt.Println("开始执行下载任务")
go Download1()
go Download2()
//给协程6秒钟的时间 防止打印也需要时间造成任务没有完成
time.Sleep(6 * time.Second)
fmt.Println("complete all")
end := time.Now()
delta := end.Sub(start)
fmt.Println("函数执行时间", delta)
}
试想若有几百个甚至几万个这样的任务
我们开启几万个协程就可以完美解决问题
对于go来说做到开启几万个协程很轻松
而其他的编程语言会占用过多的资源
而且go的语法更为简单