Golang之并发篇
进程和线程
A。进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。
B。线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。
C。一个进程可以创建和撤销多个线程;同一进程中的多个线程之间可以并发执行。
并发和并行
并发:多线程程序在一个核的cpu上运行
并行:多线程程序在多个核的cpu上运行
举例。。一个妈给一个碗给多个小孩喂饭,,是并发
一个妈给每个小孩一人一个碗,就是并行
并发 并行
协程和线程
协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级协程,这些用户级线程的调度也是自己实现的。
线程:一个线程上可以跑多个协程,协程是轻量级的线程。
例子
package main import ( "fmt" "time" ) func test() { var i int for { fmt.Println(i) time.Sleep(time.Second) i++ } } func main() { go test() //起一个协程执行test() for { fmt.Println("i : runnging in main") time.Sleep(time.Second ) } }
-
--
设置Golang运行的cpu核数。
1.8版本以上,默认跑多个核
package main import ( "fmt" "runtime" ) func main() { num := runtime.NumCPU() runtime.GOMAXPROCS(num) fmt.Println(num) }
并发计算
package main import ( "fmt" "sync" "time" ) 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 < 16; 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只能存整数
channel声明
var name chan string var age chan int var mapchan chan map[string]string var test chan student var test1 chan *student
channel初始化
使用make进行初始化 var test chan int test = make(chan int,10) var test chan string test=make(chan string,10)
channel基本操作
从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
第一个例子
package main import "fmt" type student struct { name string } func main() { var stuChan chan *student stuChan = make(chan *student, 10) stu := student{name: "stu001"} stuChan <- &stu var stu01 interface{} stu01 = <-stuChan var stu02 *student stu02, ok := stu01.(*student) if !ok { fmt.Println("can not convert") return } fmt.Println(stu02) }
goroutine与channel
package main import ( "fmt" "time" ) //起一个读的协程 func write(ch chan int) { for i := 0; i < 1000; i++ { ch <- i } } func read(ch chan int) { for { var b int b = <-ch fmt.Println(b) } } func main() { intChan := make(chan int, 10) go write(intChan) go read(intChan) time.Sleep(10 * time.Second) }
读,取,字符串
package main import ( "fmt" "time" ) 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) } } func main() { ch := make(chan string) go sendData(ch) go getData(ch) time.Sleep(3 * time.Second) }
Channel缓冲区
只能放一个元素的testChan var testChan chan int testChan = make(chan int) var a int a = <-testChan
-
testChan是带缓冲区的chan,一次可以放10个元素
var testChan chan int testChan = make(chan int, 10) var a int = 10 testChan <- a
-
package main import ( "fmt" "sync" "time" ) 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 < 16; 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() }
package main import ( "fmt" ) 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, 1000) resultChan := make(chan int, 1000) exitChan := make(chan bool, 8) go func() { for i := 0; i < 10000; 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) } }
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" 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) } }
如何关闭chan
1. 使用内置函数close进行关闭,chan关闭之后,for range遍历chan中 已经存在的元素后结束 2. 使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法 需要使用,v, ok := <- ch进行判断chan是否关闭
chan的只读和只写
只读的声明 Var 变量的名字 <-chan int Var readChan <- chan int 只写的声明 Var 变量的名字 chan<- int Var writeChan chan<- int
package main import "fmt" //只写chan func send(ch chan<- int, exitChan chan struct{}) { for i := 0; i < 10; i++ { ch <- i } close(ch) var a struct{} exitChan <- a } //只读chan 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) //初始化chan 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" import "time" func main() { var ch chan int ch = make(chan int, 10) ch2 := make(chan int, 10) go func() { var i int for { ch <- i time.Sleep(time.Second) ch2 <- i * i time.Sleep(time.Second) i++ } }() for { select { case v := <-ch: fmt.Println(v) case v := <-ch2: fmt.Println(v) case <-time.After(time.Second): fmt.Println("get data timeout") time.Sleep(time.Second) } } }
努力成为一个开发者
个人站点:www.pythonav.cn