协程
与传统的系统级线程和进程相比,协程的大优势在于其“轻量级”,可以轻松创建上百万个而不会导致系统资源衰竭,而线程和进程通常多也不能超过1万个。这也是协程也叫轻量级线程的原因。
goroutine--Go对协程的实现 :
go + 函数名:启动一个协程执行函数体
- package main
-
- import (
- "fmt"
- "time"
- )
-
- func test_Routine() {
- fmt.Println("This is one routine!!!")
- }
-
- func Add(x, y int) {
- z := x + y
- fmt.Println(z)
- }
-
- func main() {
- for i := 1; i < 10; i++ {
- go Add(i, i)
- }
-
- time.Sleep(2)
- }
channel
Go语言在语言级别提供的goroutine间的通信方式
channel的写与读:
ch <- c 写
c:= <- ch 读
阻塞 除非有goroutine对其进行操作
- package main
-
- import (
- "fmt"
- "strconv"
- )
-
- func Add(x, y int, quit chan int) {
- z := x + y
- fmt.Println(z)
-
- quit <- 1
- }
-
- func Read(ch chan int) {
- value := <-ch
-
- fmt.Println("value:" + strconv.Itoa(value))
- }
-
- func Write(ch chan int) {
- //ch <- 10
- }
-
- func main() {
- //ch := make(chan int)
- //go Read(ch)
- //go Write(ch)
-
- //time.Sleep(10)
-
- //fmt.Println("end of code")
-
- chs := make([]chan int, 10)
- for i := 0; i < 10; i++ {
- chs[i] = make(chan int)
- go Add(i, i, chs[i])
- }
-
- for _, v := range chs {
- <-v
- }
- }
缓冲channel
c := make(chan int, n) ,n为缓冲区大小
c := make(chan int) 等价于c:=make(chan int, 0)
缓冲channel的写与读:
ch <- c 写
c:= <- ch 读
缓冲满之后阻塞 除非有goroutine对其进行操作
- // 缓冲channel
- package main
-
- import (
- "fmt"
- "time"
- )
-
- var ch chan int
-
- func test_channel() {
- ch <- 1
- fmt.Println("ch 1")
- ch <- 1
- fmt.Println("ch 2")
- ch <- 1
- fmt.Println("come to end goroutine 1")
- }
-
- func main() {
- ch = make(chan int, 0) // 等价于 ch = make(chan int) 都是不带缓冲的channel
- ch = make(chan int, 2) // 是带缓冲的channel
- go test_channel()
- time.Sleep(2 * time.Second)
- fmt.Println("running end!")
- <-ch
-
- time.Sleep(time.Second)
- }
select
Linux很早就引入的函数,用来实现非阻塞的一种方式
Go语言直接在语言级别支持select关键字,用于处理异步IO 问题
select 的用法与Switch 非常类似,由select 开始一个新的选择模块,每个选择条件由case 语句来描述。
与Switch语句相比,select有比较多的限制,其中最大的一条限制就是每个case 语句必须是一个IO操作。
- select {
- case <-chan1: // 如果chan1成功读到数据,则进行该case处理语句
-
- case chan2 <- 1: // 如果成功向chan2写入数据,则进行该case处理语句
-
- default: // 如果上面都没有成功,则进入default处理流程
- }
在一个select 语句中,Go 语言会按顺序从头至尾评估每一个发送和接收的语句。
如果其中的任意一语句可以继续执行(即没有被阻塞),那么久从那些可以执行的语句中任意选择一条来使用。
如果没有任意一条语句可以执行(即所有的通道都被阻塞),那么有两种可能的情况:
- 如果给出了default语句,那么就会执行default语句,同时程序的执行会从select 语句后的语句中恢复。
- 如果没有default 语句,那么select 语句将被阻塞,直到至少有一个通信可以进行下去。
注:select 一般不写default,以免程序进入忙轮询。
- // select.go
- package main
-
- import (
- "fmt"
- "time"
- )
-
- func main() {
- ch := make(chan int)
-
- go func(ch chan int) {
- ch <- 1
- }(ch)
-
- time.Sleep(time.Second)
-
- select {
- case <-ch:
- fmt.Print("come to read ch!")
- default:
- fmt.Print("come to default!")
- }
- }
超时控制的经典实现:
- timeout := make(chan bool, 1)
- go func() {
- time.Sleep(1e9) // 等待1秒钟
- timeout <- true
- }()
-
- // 然后我们把timeout这个channel利用起来
- select {
- case <-ch: // 从ch中读取到数据
- case <-timeout: // 一直没有从ch中读取到数据,但从timeout中读取到了数据
- }
- // select.go
- package main
-
- import (
- "fmt"
- "time"
- )
-
- func main() {
- ch := make(chan int)
- timeout := make(chan int, 1)
-
- go func() {
- time.Sleep(time.Second)
- timeout <- 1
- }()
-
- select {
- case <-ch:
- fmt.Print("come to read ch!")
- case <-timeout:
- fmt.Print("come to timeout!")
- }
-
- fmt.Print("end of code!")
- }
还可以使用 time.After(...) 作为超时控制
- // select.go
- package main
-
- import (
- "fmt"
- "time"
- )
-
- func main() {
- ch := make(chan int)
-
- select {
- case <-ch:
- fmt.Print("come to read ch!")
- case <-time.After(time.Second):
- fmt.Print("come to timeout!")
- }
-
- fmt.Print("end of code!")
- }
协程与线程质的不同
协程的特点:
- 该任务的业务代码主动要求切换,即主动让出执行权
- 发生了IO,导致执行阻塞
- // goroutine_2.go
- package main
-
- import (
- "fmt"
- "runtime"
- "strconv"
- "time"
- )
-
- func main() {
- //协程1
- go func() {
- for i := 1; i < 100; i++ {
- if i == 10 {
- //主动出让cpu 使用的话 需要 导入 runtime包
- runtime.Gosched()
- }
- fmt.Println("routine 1:" + strconv.Itoa(i))
- }
- }()
-
- //协程2
- go func() {
- for i := 100; i < 200; i++ {
- fmt.Println("routine 2:" + strconv.Itoa(i))
- }
- }()
-
- time.Sleep(time.Second)
- }
- // goroutine
- package main
-
- import (
- "fmt"
- "strconv"
- "time"
- )
-
- var ch chan int
-
- func main() {
- ch = make(chan int)
- //协程1
- go func() {
- for i := 1; i < 100; i++ {
- if i == 10 {
- //遇到了阻塞
- <-ch
- }
- fmt.Println("routine 1:" + strconv.Itoa(i))
- }
- }()
-
- //协程2
- go func() {
- for i := 100; i < 200; i++ {
- fmt.Println("routine 2:" + strconv.Itoa(i))
- }
-
- ch <- 1
- }()
-
- time.Sleep(time.Second)
- }
java的多线程:
- public class thread {
- }
- }
-
- public void run() {
- for (int i = 0; i < 100; i++) {
- }
- }
- }
-
- public void run() {
- for (int i = 100; i < 200; i++) {
- }
- }
- }