天天看點

Golang之并發篇

程序和線程

A。程序是程式在作業系統中的一次執行過程,系統進行資源配置設定和排程的一個獨立機關。

B。線程是程序的一個執行實體,是CPU排程和分派的基本機關,它是比程序更小的能獨立運作的基本機關。

C。一個程序可以建立和撤銷多個線程;同一程序中的多個線程之間可以并發執行。      
Golang之并發篇
并發和并行
并發:多線程程式在一個核的cpu上運作
并行:多線程程式在多個核的cpu上運作
舉例。。一個媽給一個碗給多個小孩喂飯,,是并發
    一個媽給每個小孩一人一個碗,就是并行
      
Golang之并發篇

并發                       并行

協程和線程
協程:獨立的棧空間,共享堆空間,排程由使用者自己控制,本質上有點類似于使用者級協程,這些使用者級線程的排程也是自己實作的。
線程:一個線程上可以跑多個協程,協程是輕量級的線程。      

例子

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之并發篇

--

Golang之并發篇

設定Golang運作的cpu核數。

1.8版本以上,預設跑多個核

package main

import (
    "fmt"
    "runtime"
)

func main() {
    num := runtime.NumCPU()
    runtime.GOMAXPROCS(num)
    fmt.Println(num)
}      

并發計算

Golang之并發篇
Golang之并發篇
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)
}      

讀,取,字元串

Golang之并發篇
Golang之并發篇
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      

 -

Golang之并發篇
Golang之并發篇
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()
}      

goroutine_lock

Golang之并發篇
Golang之并發篇
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)
    }

}      

goroutine_sync

Golang之并發篇
Golang之并發篇
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
        }
    }
}      

goroutine_sync2

檢測管道是否關閉

Golang之并發篇
Golang之并發篇
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      
Golang之并發篇
Golang之并發篇
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
        }
    }

}      

隻讀隻寫示例chan

Golang之并發篇
Golang之并發篇
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)
        }
    }
}      

chan不阻塞