进程
线程
协程
设置golang运行cpu数
1.主线程和协程同时执行
package main import ( "fmt" "strconv" "time" ) func test(){ for i:=1;i<=10;i++{ fmt.Println("test() hello world"+strconv.Itoa(i)) time.Sleep(time.Second) } } //开启协程 func main(){ go test()//开启一个协程 for i:=1;i<=10;i++{ fmt.Println("main() hello,goang"+strconv.Itoa(i)) time.Sleep(time.Second) } } //主线程和协程同时执行
2.设置golang运行cpu数
package main import ( "fmt" "runtime" ) //设置golang运行cpu数 func main(){ //获取当前系统cpu数量 num:=runtime.NumCPU() //我这里设置num-1的cpu运行go程序 runtime.GOMAXPROCS(num) fmt.Println("num=",num) }
3.加锁
package main import ( "fmt" "sync"//互斥锁 ) //求1-n每个数的阶乘 var ( myMap = make(map[int]int, 10) lock sync.Mutex ) func test(n int) { res := 1 for i := 1; i <= n; i++ { res *= i } //存入map //加锁 lock.Lock() myMap[n] = res lock.Unlock() } func main() { for i := 1; i <= 60; i++ { go test(i) } //输出 //加锁避免还未生成完就进行输出 lock.Lock() for i, v := range myMap { fmt.Printf("map[%d]=%d ", i, v) } lock.Unlock() }
管道
1.管道说明
package main import "fmt" //管道 func main() { //1.创建一个存放3个int类型的管道 var intChan chan int intChan = make(chan int, 3) //看看intChan是什么 fmt.Printf("intChan 的值=%v intChan 本身的地址=%p ", intChan, &intChan) //3.向管道写入数据 intChan <- 10 num := 211 intChan <- num intChan <- 50 //intChan<-98//当我们写入数据时,不能超过其容量 //4.看看管道长度和cap(容量) fmt.Printf("channel len=%v cap=%v ", len(intChan), cap(intChan)) //5.从管道中读取数据 var num2 int num2=<-intChan fmt.Println("num2=",num2) fmt.Printf("channel len=%v cap=%v ",len(intChan),cap(intChan)) //6.在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报deadlock num3:=<-intChan num4:=<-intChan num5:=<-intChan //会报错 fmt.Println("num3=",num3,"num4=",num4,"num5=",num5) }
2.管道的读写演示
package main import ( "fmt" ) func c1() { var intChan chan int intChan = make(chan int, 3) intChan <- 10 intChan <- 20 intChan <- 10 num1 := <-intChan num2 := <-intChan num3 := <-intChan fmt.Printf("num1=%v num2=%v num3=%v", num1, num2, num3) } //存放map func c2() { var mapChan chan map[string]string mapChan = make(chan map[string]string, 10) m1 := make(map[string]string, 20) m1["city1"] = "北京" m1["city2"] = "天津" m2 := make(map[string]string, 20) m2["hero1"] = "宋江" m2["hero2"] = "武松" mapChan <- m1 mapChan <- m2 } //结构体变量 type Cat struct { Name string Age int } func chanStruct() { var catChan chan Cat catChan = make(chan Cat, 10) cat1 := Cat{Name: "tom1", Age: 18} cat2 := Cat{Name: "tom2", Age: 28} catChan <- cat1 catChan <- cat2 fmt.Println(cat1, cat2) } func chanStruct2() { var catChan chan *Cat catChan = make(chan *Cat, 10) cat1 := Cat{Name: "tom1", Age: 18,} cat2 := Cat{Name: "tom2", Age: 28,} catChan <- &cat1 catChan <- &cat2 //取出 cat11 := <-catChan cat22 := <-catChan fmt.Println(cat11, cat22) } //存放任意数据类型 func chanOther() { var allChan chan interface{} allChan = make(chan interface{}, 10) cat1 := Cat{Name: "tom1", Age: 19,} cat2 := Cat{Name: "tom2", Age: 29,} allChan <- cat1 allChan <- cat2 allChan <- 10 allChan <- "jack" //取出 cat11 := <-allChan cat22 := <-allChan v1 := <-allChan v2 := <-allChan fmt.Println(cat11, cat22, v1, v2) } //取出结构体属性需要使用类型断言 func c6() { var allChan chan interface{} allChan = make(chan interface{}, 10) cat1 := Cat{Name: "tom1", Age: 19,} cat2 := Cat{Name: "tom2", Age: 29,} allChan <- cat1 allChan <- cat2 allChan <- 10 allChan <- "jack" //取出 cat11 := <-allChan //使用类型断言,恢复结构体形式 a := cat11.(Cat) fmt.Println(a.Name) } //管道的读写演示 func main() { c6() }
3.channel遍历以及关闭
package main import "fmt" //channel遍历以及关闭 func chanClose(){ intChan :=make(chan int,3) intChan<-100 intChan<-200 close(intChan)//关闭 //关闭后,不能继续写入,但可以读取 } //channel遍历 //1.遍历时如果没有关闭,则会出现deadlock错误 //2.如果已经关闭,遍历完成后,就会退出 func chanFor(){ intChan2:=make(chan int,100) for i:=0;i<100;i++{ intChan2<-i*2 } //遍历管道不能使用for close(intChan2) for v:=range intChan2{ fmt.Println("v=",v) } } func main(){ chanFor() }
4.最佳案例1使用协程
package main import ( "fmt" "time" ) func writeData(intChan chan int) { for i := 1; i <= 50; i++ { //放入数据 intChan <- i fmt.Println("writeData", i) } close(intChan) } func readData(intChan chan int, exitChan chan bool) { for { v, ok := <-intChan //接收读取的内容,错误 //取完之后再取,返回false, //fmt.Printf("布尔值 %v ",ok) if !ok { break } fmt.Printf("readData 读取到数据=%v ", v) } //标记任务完成 exitChan <- true close(exitChan) } func main() { //创建两个管道 intChan := make(chan int, 50) exitChan := make(chan bool, 1) //启用协程 go writeData(intChan) go readData(intChan, exitChan) time.Sleep(time.Second * 1) //1秒 //每次到这里检查是否读完了 for { _, ok := <-exitChan if !ok { fmt.Println("结束了") break } } }
5.多协程取素数
package main import ( "fmt" "time" ) //统计1-n素数 //向intChan放入1-n个数 func putNum(intChan chan int) { for i := 1; i <= 8000; i++ { intChan <- i } close(intChan) } //取出数据判断是否为素数,如果是就放书primeChan func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) { //使用for循环 var flag bool for { time.Sleep(time.Millisecond * 10) num, ok := <-intChan if !ok { break } flag = true for i := 2; i < num; i++ { //不是素数 if num%i == 0 { flag = false break } } if flag { //素数存入 primeChan <- num } } fmt.Println("有一个primeNUm 协程,因为取不到数据,退出") //标记读取完成 exitChan <- true } func main() { intChan := make(chan int, 1000) primeChan := make(chan int, 2000) //标识退出的管道 exitChan := make(chan bool, 4) //开启一个协程,向intChan放入1-8000个数 go putNum(intChan) //开启四个协程,从intChan取出数据,并判断是否为素数,如果是就放入 for i := 0; i < 4; i++ { go primeNum(intChan, primeChan, exitChan) } //这里主线程 处理 go func() { for i := 0; i < 4; i++ { <-exitChan } close(primeChan) }() //遍历primeChan,把结果取出 for{ res,ok:=<-primeChan if !ok{ break } //输出 fmt.Printf("素数=%d ",res) } fmt.Println("线程退出") }
6.只读,只写
//1.channel可以生名为只读,或者只写性质 func g1(){ //1.可读可写 //var chan1 chan int //2.声明为只写 var chan2 chan<-int chan2=make(chan int,3) chan2<-20 fmt.Println("chan2=",chan2) //3.声明为只读 //var chan3 <-chan int //num2:=<-chan3 }
7.测试只读,只写
package main import ( "fmt" ) //1.channel可以生名为只读,或者只写性质 func g1() { //1.可读可写 //var chan1 chan int //2.声明为只写 var chan2 chan<- int chan2 = make(chan int, 3) chan2 <- 20 fmt.Println("chan2=", chan2) //3.声明为只读 //var chan3 <-chan int //num2:=<-chan3 } //只写 func send(ch chan<- int, exitchan chan struct{}) { for i := 0; i < 10; i++ { ch <- i } close(ch) var a struct{} exitchan <- a } //只读 func recv(ch <-chan int, exitChan chan struct{}) { for { v, ok := <-ch if !ok { break } fmt.Println(v) } var a struct{} exitChan <- a } func main() { var ch chan int ch = make(chan int, 10) exitChan := make(chan struct{}, 2) go send(ch, exitChan) go recv(ch, exitChan) var total = 0 for _ = range exitChan { total++ if total == 2 { break } } fmt.Println("结束。。。") }
8.使用select可以解决从通道取数据阻塞问题
package main import ( "fmt" "time" ) //使用select可以解决从通道取数据阻塞问题 func main(){ //定义一个管道10个数据int intChan:=make(chan int,10) for i:=0;i<10;i++{ intChan<- i } //2.定义一个管道5个数据string stringChan:=make(chan string,10) for i:=0;i<5;i++{ stringChan<-"hello"+fmt.Sprintf("%d",i) } //传统方法在遍历管道时,如果不关闭会阻塞导致deadlock //问题,在实际开发中我们不好确定什么关闭管道 //可以使用select方式可以解决 //label: for{ select{ //注意:这里,如果intChan一直没有关闭,不会一直阻塞而deadlock //会自动到下一个case匹配 case v :=<-intChan: fmt.Printf("从intChan读取的数据%d ",v) time.Sleep(time.Second) case v:=<-stringChan: fmt.Printf("从stringChan读取的数据%v ",v) time.Sleep(time.Second) default: fmt.Printf("都取不到了,程序可以加入逻辑 ") time.Sleep(time.Second) return //break label } } }
9.goroutine中使用recover,解决协程中出现panic ,导致程序崩溃
package main import ( "fmt" "time" ) //goroutine中使用recover,解决协程中出现panic ,导致程序崩溃 // func sayHello() { for i := 0; i < 10; i++ { time.Sleep(time.Second) fmt.Println("hello world") } } func test() { defer func() { //捕捉test抛出的panic if err := recover(); err != nil { fmt.Println("test() 发生错误", err) } }() //定义一个map var myMap map[int]string myMap[0] = "golang" } func main() { go sayHello() go test() for i := 0; i < 10; i++ { fmt.Println("main() ok=", i) time.Sleep(time.Second) } }
借助scanner阻塞主线程结束
func main(){ go func() { var times int for{ times++ fmt.Println("tick",times) time.Sleep(time.Second) } }() //不输入实际对主协程进行了阻塞 var input string fmt.Scanln(&input) }
多个子协程调用顺序是随机的
func printNum() { for i := 1; i < 5; i++ { time.Sleep(250 * time.Millisecond) fmt.Printf("%d", i) } } func printLetter() { for i := 'a'; i < 'e'; i++ { time.Sleep(400 * time.Millisecond) fmt.Printf("%c", i) } } func main() { go printNum() go printLetter() time.Sleep(3 * time.Second) fmt.Println(" main over") }
生产消费模型
//借助channel实现生产消费模型 func main() { ch1 := make(chan int) //生产 go producer(ch1) //消费 ch_bool1 := make(chan bool) ch_bool2 := make(chan bool) ch_bool3 := make(chan bool) go customer("jack", ch1, ch_bool1) go customer("rose", ch1, ch_bool2) go customer("anner", ch1, ch_bool3) <-ch_bool1 <-ch_bool2 <-ch_bool3 fmt.Println("main over") } func producer(ch chan int) { for i := 1; i <= 10; i++ { ch <- i fmt.Printf("生产%d号面包 ", i) time.Sleep(time.Second) } close(ch) } //消费面包 func customer(name string, ch chan int, ch_bool chan bool) { for data := range ch { fmt.Printf("%v吃了%d号面包 ", name, data) // time.Sleep(time.Second) } ch_bool <- true close(ch_bool) }
timer
延迟存入通道
func main(){ //创建计时器 //4秒后存入通道 timer1:=time.NewTimer(4*time.Second) fmt.Println(time.Now()) data:=<-timer1.C//取到之前会被阻塞 fmt.Printf("timer_t=%T ",timer1.C) fmt.Printf("data=%T ",data) fmt.Println("da",data) }
after延迟存入
//after使用 //延迟存入通道 func main() { //2.使用After(),返回值<-chan Time,同Timer.C ch1 := time.After(5 * time.Second) fmt.Println(time.Now()) data := <-ch1 fmt.Printf("data_type=%T ", data) fmt.Println("data", data) }
sync
防止主线程先于子goroutine结束
func main() { var wg sync.WaitGroup fmt.Printf("%T ", wg) wg.Add(3) rand.Seed(time.Now().UnixNano()) go printNum(&wg, 1) go printNum(&wg, 2) go printNum(&wg, 3) wg.Wait() //进入阻塞状态 defer fmt.Println("main over") } func printNum(wg *sync.WaitGroup, num int) { for i := 1; i <= 3; i++ { pre := strings.Repeat(" ", num-1) fmt.Printf("%s第%d号子goroutine,%d ", pre, num, i) time.Sleep(time.Second) } wg.Done() }
互斥锁实现购票
//互斥锁 var tickets int = 20 var wg sync.WaitGroup var mutex sync.Mutex func main() { wg.Add(4) go saleTickets("1号窗口", &wg) go saleTickets("2号窗口", &wg) go saleTickets("3号窗口", &wg) go saleTickets("4号窗口", &wg) wg.Wait() defer fmt.Println("所有車票售空") } func saleTickets(name string, wg *sync.WaitGroup) { defer wg.Done() for { //锁定 mutex.Lock() if tickets > 0 { time.Sleep(1 * time.Second) //获取窗口的编号 num, _ := strconv.Atoi(name[:1]) pre := strings.Repeat("--", num) fmt.Println(pre, name, tickets) tickets-- } else { fmt.Printf("%s 结束售票 ", name) mutex.Unlock() break } //解锁 mutex.Unlock() } }
读写互斥锁
//读写互斥锁 func main() { var rwm sync.RWMutex for i := 1; i <= 3; i++ { go func(i int) { fmt.Printf("goroutine %d,尝试读锁定。 ", i) rwm.RLock() fmt.Printf("goroutine %d 已经锁定了 ", i) time.Sleep(5 * time.Second) fmt.Printf("goroutine %d,读解锁", i) rwm.RUnlock() }(i) } time.Sleep(1 * time.Second) fmt.Println("main.,尝试写锁定") rwm.Lock() fmt.Println("main 已经锁定了") rwm.Unlock() fmt.Println("main 写解锁") }
解决资源竞争