码迷,mamicode.com
首页 > 其他好文 > 详细

goroutine

时间:2018-02-14 21:33:04      阅读:281      评论:0      收藏:0      [点我收藏+]

标签:ota   div   缓冲   string   markdown   sel   程序   只读   runtime   

进程和线程

进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。

线程是进程的一个执行实体,是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)
}

goroutine

标签:ota   div   缓冲   string   markdown   sel   程序   只读   runtime   

原文地址:https://www.cnblogs.com/gregoryli/p/8448903.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!