进程和线程
1. 进程是程序在操作系统中的⼀次执⾏过程,系统进口资源分配和调度的一个独力单位。
2. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是⽐进程更⼩的能独力运行的基本单位。
3. 一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行
Goroutine
blob.png
并发和并行
多线程程序在一个核的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("---")
}
单元测试
©著作权归作者所有:来自51CTO博客作者zhaichaoqun的原创作品,如需转载,请与作者联系,否则将追究法律责任
共同学习,写下你的评论
评论加载中...
作者其他优质文章