我们先弄懂几个名词
运行方式:
1.串行 按顺序执行。同一时刻只允许有一条指令在CPU上执行。
2.并行 一起执行。同一时刻允许多条指令在多个CPU上执行。
并发:是伪并行,是有选择的串行。
同一时间只能有一条指令在CPU上执行。但CPU会快速的在多条指令之间轮询执行。
程序:是一个可以实现特定功能的指令集。
进程:是程序在操作系统中的一次执行过程,是系统进行资源分配调度的基本单位。
例如你打开记事本程序,就建立了一个进程。再次启动又会创建一个
线程:是进程中的一个执行实例,是程序执行的最小单元。
一个进程中至少有一个线程,我们称为主线程。一个进程中除主线程,我们还可以创建销毁多个线程。这些线程可以实现特定的功能。
协程:协程是一种用户态的轻量级线程。又称微线程。英文名Goroutine
一个线程中可以有任意多额协程,但在某一刻只能有一个协程在运行,多个协程分享所在线程分配到的计算机资源。
在协程中,调用一个任务就像调用一个函数一样,消耗极少的资源,但能达到进程、线程相同的并发效果。
package main
import(
"fmt"
"time"
)
func sing(){
for i:=0;i<10;i++{
fmt.Println("我在吹口哨")
time.Sleep(time.Millisecond)
}
}
func dance(){
for i:=0;i<10;i++{
fmt.Println("我在跳舞")
time.Sleep(time.Millisencond)
}
}
func main(){
//串行
sing()
dance()
//并行。接着奏乐接着舞
go sing() //开启了一个协程
go dance() //又开启一个协程
for{ //主线程结束,程序会退出。所以这里使用死循环。
;
}
}
runtime包中常用的函数
Gosched()使当前go程放弃处理器,以让其他go程运行
package main
import(
"fmt"
"runtime"
)
func sing(){
for i:=0;i<10;i++{
fmt.Println("我在吹口哨")
runtime.Gosched() //放弃处理器,让其他go程运行,不会挂起当前go程,未来会恢复
}
}
func dance(){
for i:=0;i<10;i++{
fmt.Println("我在跳舞")
runtime.Gosched()
}
}
func main(){
//一边奏乐一边舞
go singg()
go dance()
for{
;
}
}
Goexit()终止调用它的go程,其他go程不受影响
package main
import(
"fmt"
"runtime"
)
func main(){
go func(){
fmt.Println("123")
//return 退出当前函数
//runtime.Goexit() 退出当前协程
test()
fmt.Println("456")
}()
for{
;
}
}
func test(){
fmt.Println("abc")
//return 只会结束当前函数,协程中的其他的代码会继续执行
runtime.Goexit() //会结束整个协程,协程中之后的代码都不会执行
fmt.Println("def")
}
NumCPU与GOMAXPROCS设置可以同时执行额最大CPU数,并返回先前的设置
package main
import(
"fmt"
"runtime"
)
func main(){
num:=runtime.NumCPU();
fmt.Println(num)
runtime.GOMAXPROCS(num)
}
多线程打的同步问题
互斥锁
互斥锁的本质是一个个goroutine访问的时候,其他goroutine不能访问
这样就实现资源同步,但是避免资源竞争的同时也降低了程序的并发性能,程序由原来的并发执行变成了串行。
package main
import(
"fmt"
"sync"
"time"
)
//创建一把互斥锁
var lock sync.Mutex
func printer(str string) {
//拿到执行上锁
lock.Lock()
for_,v := range str{
fmt.Printf("%c",v)
time.sleep(time.Millisecond*500)
}
lock.Unlock()
}
func personl(){
printer("hello")
}
func person2(){
printer("world")
}
func main(){
go person1()
go person2()
for{
;
}
}