介绍安全
线程是cpu调度的最小单位,只有不一样的线程才能同时在多核cpu上同时运行。但线程太占资源,线程调度开销大。go中的goroutine是一个轻量级的线程,执行时只须要4-5k的内存,比线程更易用,更高效,更轻便,调度开销比线程小,可同时运行上千万个并发。 go语言中开启一个goroutine很是简单,go函数名(),就开启了个线程。并发
默认状况下,调度器仅使用单线程,要想发挥多核处理器的并行处理能力,必须调用runtine.GOMAXPROCS(n)来设置可并发的线程数,也能够经过设置环境变量GOMAXPROCS打到相同的目的。函数
goroutineui
Runtime包中提供了几个与goroutine相关的函数。Gosched()让当前正在执行的goroutine放弃CPU执行权限。调度器安排其余正在等待的线程运行。 请看如下例子:this
package main线程
import ( "runtime" "fmt" )3d
func main(){ go sayHello() go sayWorld() var str string fmt.Scan(&str) }调试
func sayHello(){ for i := 0; i < 10; i++{ fmt.Print("hello ") runtime.Gosched() } }队列
func sayWorld(){ for i := 0; i < 10; i++ { fmt.Println("world") runtime.Gosched() } } 运行结果 从上面输出结果可知,咱们启动了两个线程,其中一个线程输出一句后调用Gosched函数,释放CPU权限;以后另外一个线程得到CPU权限。这样两个线程交替得到cpu权限,才输出了以上结果。进程
runtime.NumCPU()返回了cpu核数,runtime.NumGoroutine()返回当前进程的goroutine线程数。即使咱们没有开启新的goroutine。
package main
import ( "runtime" "fmt" ) func main(){ fmt.Println(runtime.NumCPU()) fmt.Println(runtime.NumGoroutine()) } 运行结果 runtime.Goexit()函数用于终止当前的goroutine,单defer函数将会继续被调用。
package main
import ( "runtime" "fmt" )
func test(){ defer func(){ fmt.Println(" in defer") }() for i := 0; i < 10; i++{ fmt.Print(i) if i > 5{ runtime.Goexit() } } }
func main(){ go test() var str string fmt.Scan(&str) } 运行结果 在这里你们或许有个疑问,下面这两句代码干吗的呢
var str string fmt.Scan(&str) 这两句代码是等待输入的意思,在这里用来阻止主线程关闭的。若是没有这两句的话,会发现咱们的程序瞬间就结束了,并且什么都没有输出。这是由于主线程关闭以后,全部开启的goroutine都会强制关闭,他尚未来得及输出,就结束了。 可是这样感受怪怪的。若是有一种机制,在子线程结束的时候通知一下主线程,而后主线程再关闭,岂不是更好,这样就不用无休止的等待了。因而就有了channel。
channel
goroutine之间经过channel来通信,能够认为channel是一个管道或者先进先出的队列。你能够从一个goroutine中向channel发送数据,在另外一个goroutine中取出这个值。 使用make建立
var channel chan int = make(chan int) // 或 channel := make(chan int) 生产者/消费者是最经典的使用示例。生产者goroutine负责将数据放入channel,消费者goroutine从channel中取出数据进行处理。
package main
import ( "fmt" )
func main(){ buf:=make(chan int) flg := make(chan int) go producer(buf) go consumer(buf, flg) <-flg //等待接受完成 }
func producer(c chan int){ defer close(c) // 关闭channel for i := 0; i < 10; i++{ c <- i // 阻塞,直到数据被消费者取走后,才能发送下一条数据 } }
func consumer(c, f chan int){ for{ if v, ok := <-c; ok{ fmt.Print(v) // 阻塞,直到生产者放入数据后继续读取数据 }else{ break } } f<-1 //发送数据,通知main函数已接受完成 } 运行结果
能够将channel指定为单向通讯。好比<-chan int仅能接收,chan<-int仅能发送。以前的生产者消费者能够改成一下方式: func producer(c chan<-int){ defer close(c) // 关闭channel for i := 0; i < 10; i++{ c <- i // 阻塞,直到数据被消费者取走后,才能发送下一条数据 } }
func consumer(c <-chan int, f chan<-int){ for{ if v, ok := <-c; ok{ fmt.Print(v) // 阻塞,直到生产者放入数据后继续读取数据 }else{ break } } f<-1 //发送数据,通知main函数已接受完成 } channle能够是带缓冲的。make的第二个参数做为缓冲长度来初始化一个带缓冲的channel:
c := make(chan int, 5) 向带缓冲的channel发送数据时,只有缓冲区满时,发送操做才会被阻塞。当缓冲区空时,接收才会阻塞。 能够经过如下程序调整发送和接收的顺序调试
package main
import ( "fmt" )
func main(){ c := make(chan int, 2) c <- 1 c <- 2 fmt.Println(<-c) fmt.Println(<-c) } select
若是有多个channel须要监听,能够考虑用select,随机处理一个可用的channel
package main
import ( "fmt" )
func main(){ c := make(chan int) quit := make(chan int) go func(){ for i := 0; i < 10; i++{ fmt.Printf("%d ", <-c) } quit <- 1 }() testMuti(c, quit) }
func testMuti(c, quit chan int){ x, y := 0, 1 for { select{ case c<-x: x, y = y, x+y case <-quit: fmt.Print("\nquit") return } } } 运行结果 channle超时机制
当一个channel被read/write阻塞时,会被一直阻塞下去,直到channel关闭。产生一个异常退出程序。channel内部没有超时的定时器。但咱们能够用select来实现channel的超时机制
package main
import ( "time" "fmt" )
func main(){ c := make(chan int) select{ case <- c: fmt.Println("没有数据") case <-time.After(5* time.Second): fmt.Println("超时退出") } } 运行结果 线程同步
假设如今咱们有两个线程,一个线程写文件,一个线程读文件。若是在读文件的同时,写文件的线程向文件中写数据,就会出现问题。为了保证可以正确的读写文件,在读文件的时候,不能进行写入文件的操做,在写入时,不能进行读的操做。这就须要互斥锁。互斥锁是线程间同步的一种机制,用了保证在同一时刻只用一个线程访问共享资源。go中的互斥锁在sync包中。下面是个线程安全的map:
package main
import ( "errors" "sync" "fmt" )
func main(){ m := &MyMap{mp:make(map[string]int), mutex:new(sync.Mutex)} go SetValue(m) go m.Display() var str string fmt.Scan(&str) }
type MyMap struct{ mp map[string]int mutex *sync.Mutex }
func (this *MyMap)Get(key string)(int, error){ this.mutex.Lock() i, ok := this.mp[key] this.mutex.Unlock() if !ok{ return i, errors.New("不存在") } return i, nil }
func (this *MyMap)Set(key string, val int){ this.mutex.Lock() defer this.mutex.Unlock() this.mp[key] = val }
func (this *MyMap)Display(){ this.mutex.Lock() defer this.mutex.Unlock() for key, val := range this.mp{ fmt.Println(key, "=", val) } }
func SetValue(m *MyMap){ var a rune a = 'a' for i := 0; i< 10; i++{ m.Set(string(a+rune(i)), i) } } 运行结果 完
做者:ChainZhang 连接:https://www.jianshu.com/p/c3d65105fa46 來源:简书 著做权归做者全部。商业转载请联系做者得到受权,非商业转载请注明出处。