Go(8[Goroutine | Channel | 读写锁 | 异常处理 ])

发布时间:2020-07-11 06:46:12 作者:zhaichaoqun
来源:网络 阅读:4322

进程和线程

1. 进程是程序在操作系统中的⼀次执⾏过程,系统进口资源分配和调度的一个独力单位。

2. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的能独力运行的基本单位。

3. 一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行

Goroutine


Go(8[Goroutine | Channel  | 读写锁 | 异常处理 ])

并发和并行

设置GO运行的CPU核数

func Test1()  {
    num := runtime.NumCPU()
    //设置程序运行占几个核
    runtime.GOMAXPROCS(num)
    fmt.Println(num)
}

线程同步

sync.WaitGroup

package main

import (
	"fmt"
	"time"
	"sync"
)

var wailtGroup  sync.WaitGroup
//sync 线程同步
func Test2(index int)  {
	for i:=0;i<100;i++{
		time.Sleep(time.Millisecond)
	}
	wailtGroup.Done()
}
func calc()  {
	start :=time.Now().UnixNano()
	//Test2(0)
	for i:=0; i<3;i++{
		go Test2(i)
		wailtGroup.Add(1)
	}
        #当wailtGroup 为0时,就会返回
	wailtGroup.Wait()
	end := time.Now().UnixNano()

	fmt.Printf("finished,cost:%d ms \n",(end - start) / 1000 / 1000)
}

func main()  {
	//Test1()
	calc()
}


不同goroutine之间通信

  1. 全局变量和锁同步

  2. Channel


Channel

  1. 类似unix中的管道(pipe)

  2. 先进先出

  3. 线程安全,多个goroutine同时访问,不需要加锁

  4. Channel是有类型的,一个整数的channel只能存放整数

Channel声明:

    var 变量名 chan 类型

                var test chan int

    var test chan string

    var test chan map[string]string

    var test chan stu

    var test chan *stu

Channel初始化:

  1. 不带缓冲区:默认就是0,必须有取值,才可以放入,不然就会阻塞!

  2. 带缓冲区: 值>0

            使用make进行初始化

            var test chan int

            test =make(chan int,10) 

             var test2 chan string

            test = make(chan string,10)

 Channel基本操作:

1.从channel读取数据

testChan :=make(chan int,10)
var a int
a = <- testChan

2.从channel写数据

testChan:=make(chan int,10)
var a int=  10
testChan <- a

栗子:

1.初级操作

func test()  {
    var intChan chan int = make(chan  int,3)
    go func(){
        fmt.Println("begin input to chan\n")
        intChan <- 20
        intChan <- 20
        intChan <- 20
        fmt.Println("end input to chan\n")
    }()
    result := <- intChan
    fmt.Printf("--result:%d",result)
    time.Sleep(2*time.Second)
}
func main()  {
    test()
}

2.goroutine和channel结合

package main
import (
"fmt"
"time"
)
func main() {
    ch := make(chan string)
    go sendData(ch)
    go getData(ch)
    //加time原因是让2个go去执行, 因为主线程中的代码是比goroutine先执行完毕的
    time.Sleep(100 * time.Second)
}
func sendData(ch chan string) {
        ch <- "Washington"
        ch <- "Tripoli"
        ch <- "London"
        ch <- "Beijing"
        ch <- "Tokio"
}
func getData(ch chan string) {
        var input string
        for {
        input = <-ch
        fmt.Println(input)
        }
}

for循环去遍历chan

package main
import (
"fmt"
"time"
)
func main() {
    ch := make(chan string)
    go sendData(ch)
    go getData(ch)
    time.Sleep(100 * time.Second)
}
func sendData(ch chan string) {
    ch <- "Washington"
    ch <- "Tripoli"
    ch <- "London"
    ch <- "Beijing"
    ch <- "Tokio"
}
func getData(ch chan string) {
    for input := range ch {
        fmt.Println(input)
    }
}

Channel关闭            

  1. 使用内置函数close进行关闭,chan关闭后。for range遍历chan中已经存在的元素后结束

    1. func rangegetData(ch chan string)  {
          //另外用法,用来取管道中的数据
          //
          for input:=range ch{
              fmt.Printf("#%v\n",input)
          }
      }
  2. 使用内置函数close进行关闭,chan关闭后没有使用for range的写法需要使用,v,ok := <-ch 进行判断chan是否关闭

    1. func getData(ch chan  string)  {
          //死循环
          for {
              input,ok :=<- ch
              //ok是判断管道是否关闭
              if !ok{
                  fmt.Printf("管道关闭")
                  break
              }
              fmt.Printf("channe_read:%s\n",input)
          }
      }


    2. 进阶栗子:

      1. func consumer(ch  <-chan string){
           for{
              str,ok := <- ch
              if !ok{
                 fmt.Printf("ch is closed!!")
                 break
              }
              fmt.Printf("value is %s \n",str)
           }
        }
        func main(){
        
        var ch chan string = make(chan string)
        consumer(ch)}


Channel只读/只写

只读chan声明

var 变量名字 <-chan int
var readChan <-chan int

只写chan声明

var 变量名字 chan<-int
var writeChan chan<-int

Channel Select管理

注意:调度是随机的

一个简单的栗子:

for {
   str := fmt.Sprintf("hello %d",i)
   //select来管理管道
   //调度是随机的,
   select {
   case ch <- str:
   case exit = <-exitChan:
   }
   if exit{
      fmt.Printf("user notify exitedd!!\n")
      break
   }
}

定时器

规定时间后运行代码

package main

import (
   "time"
   "fmt"
)

func run()  {
    t:=time.NewTicker(time.Second * 5)
    //t.C 是一个管道
    for v:=range t.C{
      fmt.Println("hello",v)
    }

}
func main()  {
   run()
}

只运行一次:

package main
import (
   "fmt"
   "time"
)
func main() {

   select {
      case <- time.After(time.Second):
      fmt.Println("after")
   }
}

超时控制 (可以用于检测类似Mysql查询超时等):

package main

import (
   "time"
   "fmt"
)

func queryDB(ch chan  int)  {
   time.Sleep(time.Second * 1000)
   ch <- 100
}
func main()  {
   ch :=make(chan int,5)
   go queryDB(ch)
   //设置主线程运行时间,
   t := time.NewTicker(time.Second * 5 )
   //随机调度
   select {
       //去ch中取,是否有数据,
      case <-ch:
         fmt.Println("reslt")
       
      case <-t.C:
         fmt.Printf("timeout!!!")
   }

}

读写锁:

写锁:sync.Mutex 是互斥锁, 多个线程修改数据的适合,只有一个线程可以修改

读锁:sync.RWMutex 读锁,多个线程同时读一份数据,可以并发的去执行


Go中使用recover

应⽤场景,如果某个goroutine panic了,⽽且这个goroutine⾥⾯没有捕获(recover),那么整个进程就会挂掉。所以,好的习惯是每当go⽣⼀个goroutine,就需要写下recover

package main

import (
   "time"
   "fmt"
)

func calc()  {
   //捕获异常
   //必须写在最前面
   defer func() {
      error :=recover()
      if error !=nil{
         fmt.Println(error)
      }


   }()
   var p *int
   //p = new(int)
   *p = 100
}
func main()  {
   go calc()
   time.Sleep(time.Second * 2)
   fmt.Println("---")
}

单元测试

推荐阅读:
  1. Beego+go-sqlite3 Arm平台移植遇到的问题
  2. 再谈 Go 语言在前端的应用前景

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

go goroutine 异常处

上一篇:shell   if 详解

下一篇:使用Spring AOP切面解决数据库读写分离

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》