进程和线程
进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
一个进程可以创建和撤销多个线程;同一个进程中的多个线程之间可以并发执行.
并发和并行
多线程程序在一个核的cpu上运行,就是并发
多线程程序在多个核的cpu上运行,就是并行
协程和线程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的
线程:一个线程上可以跑多个协程,协程是轻量级的线程。
goroutine调度模型
简单的例子
package main
import "fmt"
import "time"
func test() {
var i int
for {
fmt.Println(i)
time.Sleep(time.Second*5)
i++
}
}
func main() {
go test()
for {
fmt.Println("i‘ running in main")
time.Sleep(time.Second)
}
}
设置golang运行的cpu核数
go1.8版本以上默认设置了
package main
import (
"fmt"
"runtime"
)
func main() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num)
fmt.Println(num)
}
不同goroutine之间通讯
全局变量和锁同步
package main
import (
"fmt"
"time"
"sync"
)
var (
m = make(map[int]uint64)
lock sync.Mutex
)
type task struct {
n int
}
func calc(t *task) {
var sum uint64
sum = 1
for i:=1;i<t.n;i++{
sum *= uint64(i)
}
fmt.Println(t.n,sum)
lock.Lock()
m[t.n] = sum
lock.Unlock()
}
func main() {
for i :=0;i<20;i++{
t := &task{n:i}
go calc(t)
}
time.Sleep(10 * time.Second)
/*
lock.Lock()
for k,v := range m{
fmt.Printf("%d!=%v\n",k,v)
}
lock.Unlock()*/
}
Channel
channel
a. 类似unix中管道(pipe)
b. 先进先出
c. 线程安全,多个goroutine同时访问,不需要加锁
d. 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初始化
使用make进行初始化,比如:
var test chan int
test = make(chan int, 10)
var test chan string
test = make(chan string, 10)
从channel读取数据
var testChan chan int
testChan = make(chan int, 10)
var a int
a = <- testChan
向channel写入数据
var testChan chan int
testChan = make(chan int, 10)
var a int = 10
testChan <- a
goroutine和channel相结合
package main
import (
"fmt"
"time"
)
func write(ch chan int) {
for i := 0; i < 100; i++ {
ch <- i
fmt.Println("put data:", i)
}
}
func read(ch chan int) {
for {
var b int
b = <-ch
fmt.Println(b)
time.Sleep(time.Second)
}
}
func main() {
intChan := make(chan int, 10) //testChan是带缓冲区的chan,一次可以放10个元素
go write(intChan)
go read(intChan)//不读的话会引发阻塞
time.Sleep(10 * time.Second)
}
chan之间的同步
package main
import "fmt"
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
}
}
}
例子:
package main
import (
"fmt"
)
//计算1000以内的素数
func calc(taskChan chan int, resChan chan int, exitChan chan bool) {
for v := range taskChan {
flag := true
for i := 2; i < v; i++ {
if v%i == 0 {
flag = false
break
}
}
if flag {
resChan <- v
}
}
fmt.Println("exit")
exitChan <- true
}
func main() {
intChan := make(chan int, 100)
resultChan := make(chan int, 100)
exitChan := make(chan bool, 8)
go func() {
for i := 0; i < 1000; i++ {
intChan <- i
}
close(intChan)
}()
for i := 0; i < 8; i++ {
go calc(intChan, resultChan, exitChan)
}
//等待所有计算的goroutine全部退出
go func() {
for i := 0; i < 8; i++ {
<-exitChan
fmt.Println("wait goroute ", i, " exited")
}
close(resultChan)
}()
for v := range resultChan {
//fmt.Println(v)
_ = v
}
}
chan的关闭
1.使用内置函数close进行关闭,chan关闭之后,for range遍历chan中已经存在的元素后结束
2.使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法需要使用,v, ok := <- ch进行判断chan是否关闭
package main
import "fmt"
func main() {
var ch chan int
ch = make(chan int, 10)
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
for {
var b int
b,ok := <-ch
if ok == false {
fmt.Println("chan is close")
break
}
fmt.Println(b) //谨防死循环
}
}
for range遍历chan
package main
import "fmt"
func main() {
var ch chan int
ch = make(chan int, 1000)
for i := 0; i < 1000; i++ {
ch <- i
}
close(ch)
for v := range ch {
fmt.Println(v)
}
}
chan的只读和只写
a. 只读chan的声明
Var 变量的名字 <-chan int
Var readChan <- chan int
b. 只写chan的声明
Var 变量的名字 chan<- int
Var writeChan chan<- int
对chan进行select操作
select {
case u := <- ch1:
case e := <- ch2:
default:
}
下面是死锁了,阻塞了
package main
import "fmt"
func main() {
var ch chan int
ch = make(chan int,10)
for i :=0;i<10;i++{
ch <- i
}
for {
var b int
b = <-ch
fmt.Println(b)
}
}
select解决阻塞
//@Time : 2018/2/1 22:14
//@Author: ningxin
package main
import (
"fmt"
"time"
)
func main() {
var ch chan int
ch = make(chan int,10)
for i :=0;i<10;i++{
ch <- i
}
for {
select{
case v:= <-ch:
fmt.Println(v)
default:
fmt.Println("get data timeout")
time.Sleep(time.Second)
}
}
}
定时器的使用
package main
import (
"fmt"
"time"
)
func queryDb(ch chan int) {
time.Sleep(time.Second)
ch <- 100
}
func main() {
ch := make(chan int)
go queryDb(ch)
t := time.NewTicker(time.Second)
select {
case v := <-ch:
fmt.Println("result", v)
case <-t.C:
fmt.Println("timeout")
}
}
超时控制
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num - 1)
for i := 0; i < 16; i++ {
go func() {
for {
t := time.NewTicker(time.Second)
select {
case <-t.C:
fmt.Println("timeout")
}
t.Stop()
}
}()
}
time.Sleep(time.Second * 100)
}
goroutine中使用recover
应用场景,如果某个goroutine panic了,而且这个goroutine里面没有捕获(recover),那么整个进程就会挂掉。所以,好的习惯是每当go产生一个goroutine,就需要写下recover
package main
import (
"fmt"
"runtime"
"time"
)
func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("panic:", err)
}
}()
var m map[string]int
m["stu"] = 100
}
func calc() {
for {
fmt.Println("i‘m calc")
time.Sleep(time.Second)
}
}
func main() {
num := runtime.NumCPU()
runtime.GOMAXPROCS(num - 1)
go test()
for i := 0; i < 2; i++ {
go calc()
}
time.Sleep(time.Second * 10000)
}