Go(8[Goroutine|Channel|读写锁|异常处理])
进程和线程
公司主营业务:成都做网站、网站设计、移动网站开发等业务。帮助企业客户真正实现互联网宣传,提高企业的竞争能力。成都创新互联是一支青春激扬、勤奋敬业、活力青春激扬、勤奋敬业、活力澎湃、和谐高效的团队。公司秉承以“开放、自由、严谨、自律”为核心的企业文化,感谢他们对我们的高要求,感谢他们从不同领域给我们带来的挑战,让我们激情的团队有机会用头脑与智慧不断的给客户带来惊喜。成都创新互联推出陵川免费做网站回馈大家。
1. 进程是程序在操作系统中的⼀次执⾏过程,系统进口资源分配和调度的一个独力单位。
2. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的能独力运行的基本单位。
3. 一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行
Goroutine
并发和并行
多线程程序在一个核的CPU上运行,就是并发
多线程程序在多个核的CPU上运行,就是并发
设置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之间通信
全局变量和锁同步
Channel
Channel
类似unix中的管道(pipe)
先进先出
线程安全,多个goroutine同时访问,不需要加锁
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初始化:
不带缓冲区:默认就是0,必须有取值,才可以放入,不然就会阻塞!
带缓冲区: 值>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关闭
使用内置函数close进行关闭,chan关闭后。for range遍历chan中已经存在的元素后结束
func rangegetData(ch chan string) { //另外用法,用来取管道中的数据 // for input:=range ch{ fmt.Printf("#%v\n",input) } }
使用内置函数close进行关闭,chan关闭后。没有使用for range的写法需要使用,v,ok := <-ch 进行判断chan是否关闭
func getData(ch chan string) { //死循环 for { input,ok :=<- ch //ok是判断管道是否关闭 if !ok{ fmt.Printf("管道关闭") break } fmt.Printf("channe_read:%s\n",input) } }
进阶栗子:
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("---") }
单元测试
分享文章:Go(8[Goroutine|Channel|读写锁|异常处理])
本文来源:http://pwwzsj.com/article/joigie.html