goroutine
goroutine:
本质是协程,是实现并行计算的核心。
goroutine使用方式非常的简单,只需使用go关键字即可启动一个协程,并且它是处于异步方式运行,你不需要等它运行完成以后在执行以后的代码。
启动一个协程
go func()//通过go关键字启动一个协程来运行函数
go协程和channel通信
func NumCPU() int
NumCPU返回本地机器的逻辑CPU个数。
func GOMAXPROCS(n int) int
GOMAXPROCS设置可同时执行的最大CPU数,并返回先前的设置。 若 n < 1,它就不会更改当前设置。本地机器的逻辑CPU数可通过 NumCPU 查询。本函数在调度程序优化后会去掉
常用channel操作
//1.创建一个可以存放3个int类型的管道
var intChan chan int
intChan = make(chan int, 3)
//2.了解管道
fmt.Printf("intChane 的值= %v intChan本身的地址=%p\n", intChan, &intChan)
//3.向管道写入数据
intChan <- 10
num:=211
intChan <- num
//注意向管道写入数据不能超过限定长度,不能像动态数组一样.
//4.看管道的长度和cap(容量)
fmt.Printf("channel len= %v cap= %v\n",len(intChan),cap(intChan));
//5.从管道中读取数据
var num2 int
num2= <-intChan
fmt.Println(num2)
fmt.Printf("channel len= %v cap= %v\n",len(intChan),cap(intChan));
//注意再没有使用协程的情况下数据全部取完就会报错.
func close(c chan<- Type)
关闭后后不能写只能读.
//遍历
intChan2 := make(chan int, 100)
for i := 0; i < 50; i++ {
intChan2 <- i //放入一百个数据管道
}
//遍历管道不能使用普通的for循环
close(intChan2)
for v := range intChan2 {
fmt.Printf("v= %d", v)
}
package main
import (
"fmt"
"time"
)
func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("Error ")
}
}()
var myMap map[int]string
myMap[1] = "ddd"
}
func main() {
go test()
time.Sleep(time.Second)
}
会输出Error
存放任意类型
package main
import (
"fmt"
)
type Cat struct {
Name string
Age int
}
func main() {
allchan := make(chan interface{}, 3)
allchan <- 10
allchan <- "tom jack"
cat := Cat{"小花猫", 4}
allchan <- cat
<-allchan
<-allchan
newcat := <-allchan //管道取出的是???
fmt.Printf("newcat %T ,newcat %v\n", newcat, newcat)
//fmt.Printf("newcat %T",newcat.Name)会报错
//使用类型断言
a := newcat.(Cat)
fmt.Printf("%T", a.Name)
}
案例
案例1
package main
import (
"fmt"
"strconv"
"time"
)
func test(){
for i:=0;i<=10;i++{
fmt.Println(" test() hello,world"+strconv.Itoa(i));
time.Sleep(time.Second)
}
}
func main() {
go test();//开启一个协程
for i:=0;i<=10;i++{
fmt.Println("main() hello,world"+strconv.Itoa(i));
time.Sleep(time.Second);
}
}
案例2
全局变量+锁解决
package main
import (
"fmt"
"sync"
"time"
)
//这里声明全局变量
var(
myMap=make(map[int]int)
lock sync.Mutex//互坼锁
)
func test(n int){
lock.Lock();
res:=1
for i:=1;i<=n;i++{
res*=i;
}
myMap[n]=res;
lock.Unlock();
}
func main() {
for i:=1;i<=20;i++{
go test(i)
}
time.Sleep(8000)
for i,v:=range myMap {
lock.Lock();
fmt.Printf("%d %d\n",i,v);
lock.Unlock();
}
}
channel实现思路
本质就是一个数据结构---------FIFO(first in first out)队列(先进先出)
线程安全:多个协程操作管道是线程安全的。
1)前面使用全局变量加锁同步来解决 goroutine 的通讯,但不完美
2)主线程在等待所有goroutine 全部完成的时间很难确定,我们这里设置10秒,仅仅是估算。
3)如果主线程休眠时间长了,会加长等待时间,如果等待时间短了,可能还有goroutine处于工作状态,这时也会随主线程的退出而销毁
4)通过全局变量加锁同步来实现通讯,也并不利用多个协程对全局变量的读写操作。
5)上面种种分析都在呼唤一个新的通讯机制-channel
案例3
package main
import(
"fmt")
//write Date
func writeDate(intChan chan int){
for i:=1;i<=50;i++{
//放入数据
intChan<-i
fmt.Printf("writeDate 写数据=%v\n",i)
}
close(intChan);
}
//read Date
func readDate(intChan chan int,exitChan chan bool){
for{
v,ok:=<-intChan
if !ok{
break
}
fmt.Printf("readDate 读到数据=%v\n",v)
}
//read_Date 读取完数据后,任务完成
exitChan<-true
close(exitChan)
}
func main() {
//创建两个管道
intChan:=make(chan int,50)
exitChan:=make(chan bool,1)
go writeDate(intChan)
go readDate(intChan,exitChan);
for{
_,ok:=<-exitChan
if !ok{
break;
}
}
}
阻塞
package main
import(
"fmt")
func main() {
//使用select可以解决从管道取数据的阻塞问题
intChan:=make(chan int,10)
for i:=0;i<10;i++{
intChan<-i
}
stringChan:=make(chan string,5)
for i:=0;i<5;i++{
stringChan<-"hello"+fmt.Sprintf("%d",i);
}
//label
label:
for{
select {
case v:=<-intChan://如果管道一直没关闭,不会一直阻塞deadlock
fmt.Println("从intChan读取的数据%d\n",v)
case v:=<-stringChan:
fmt.Println("从stringChan读取的数据%s\n",v)
default:
break label
}
}
}
素数案例
package main
import (
"fmt"
"time"
)
//向 intChan 放入 1-8000个数
func putNum(intChan chan int) {
for i := 1; i <= 80000; i++ {
intChan <- i
}
//关闭intChan
close(intChan)
}
func primesNum(intChan chan int, primesChan chan int, exitChan chan bool) {
for {
num, ok := <-intChan
if ok == false {
break
}
flag := false
//判断素数
for i := 2; i <= num/i; i++ {
if num%i == 0 {
flag = true
break
}
}
if flag == true {
primesChan <- num
}
}
//这里还不能关闭primesChan
exitChan <- true
}
func main() {
intChan := make(chan int, 800)
primesChan := make(chan int, 800000) //放入结结果
//退出标记
exitChan := make(chan bool, 4) //4个
start := time.Now().UnixNano()
//开启协程,用intChan放入 1-8000个数
go putNum(intChan)
//开启4个协程,从intChan取数
for i := 0; i < 4; i++ {
go primesNum(intChan, primesChan, exitChan)
}
for i := 0; i < 4; i++ {
<-exitChan
}
close(exitChan)
close(primesChan)
fmt.Println("结束所有协程")
fmt.Println("耗时毫秒 \n", (time.Now().UnixNano()-start)/1000000)
}
本文作者:可可爱爱奇奇怪怪。。。
本文链接:https://www.cnblogs.com/ccsuf/p/16263796.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。