Go語言系列(八)- Goroute和Channel

1、Goroute

1. 進程和線程

  •   A. 進程是程序在操做系統中的一次執行過程,系統進行資源分配合調度的一個獨立單位python

  •   B. 線程是進程的一個執行實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。golang

  •   C. 一個進程能夠建立和撤銷多個線程:同一個進程中的多個線程之間能夠併發執行。json

2. 併發和並行

  •   A. 多線程程序在一個核的cpu上運行,就是併發緩存

  •   B. 多線程程序在多個核的cpu上運行,就是並行安全

3. 協程和線程

  • 協程:獨立的棧空間,共享棧空間,調度由用戶本身控制,本質上有點相似於用戶級線程,這些用戶級線程的調度也是本身實現的多線程

  • 線程:一個線程上能夠拍多個協程, 協程是輕量級的線程併發

package main

import (
    "fmt"
    "time"
)

func test() {
    var i int
    for {
        fmt.Println(i)
        time.Sleep(time.Second)
        i++
    }
}

func main() {
    go test()
    time.Sleep(time.Second * 10)
    // for {
    //     fmt.Println("i' running in main")
    //     time.Sleep(time.Second)
    // }
}
goroutine

 4. goroutine調度模型

 

 5. 如何設置golang運行的cpu核數

package main

import (
    "fmt"
    "sync"
    "time"
)

type task struct {
    n int
}

var (
    m    = make(map[int]uint64)
    lock sync.Mutex // 互斥鎖
)

func calc(t *task) {
    var sum uint64
    sum = 1
    for i := uint64(1); i < uint64(t.n); i++ {
        sum *= 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(time.Second * 10)
    lock.Lock()
    for k, v := range m {
        fmt.Printf("%d! = %v\n", k, v)
    }
    lock.Unlock()
}
goroute_lock

2、Channel

1.不一樣的goroutine之間如何進行通信?

  a. 全局變量和鎖同步app

  b. Channelide

2. channel概念

  a. 相似unix中管道(pipe)函數

  b. 先進先出

  c. 線程安全,多個goroutine同時訪問,不須要de加鎖

  d. channel是有類型的,一個整數的channel只能存放整數

 3. 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

4. channel初始化

使用make進行初始化,好比:

var test chan int

test = make(chan int, 10)



var test chan string

test = make(chan string, 10)

5. channel基本操做

1. 從channel讀取數據
var testChan chan int
testChan = make(chan int, 10)
var a int
a = <- testChan
2. 從channel寫入數據
var testChan chan int
testChan = make(chan int, 10)
var a int = 10
testChan <- a
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)
    go write(intChan)
    go read(intChan)

    time.Sleep(time.Second * 10)
}
goroutine_chan

6.channel阻塞

7. 帶緩衝區的channel

  1. 以下所示,testChan 只能放一個元素

testChan := make(chan int)
var a int
a = <- testChan    

  2. 以下所示,testChan是帶緩衝區的chan,一次能夠放10個元素

testChan = make(chan int, 10)
var a int
testChan <- a
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
        }
    }
}
channel_readline
package main

import "fmt"

type student struct {
    name string
}

func main() {
    var intChan chan int
    intChan = make(chan int, 10)
    intChan <- 10

    var stringChan chan map[string]string
    stringChan = make(chan map[string]string, 10)
    m := make(map[string]string, 16)
    m["stu01"] = "001"
    m["stu01"] = "002"
    stringChan <- m

    var stuChan chan *student
    stuChan = make(chan *student, 10)
    stu := student{name: "stud01"}
    stuChan <- &stu

    var stuInterChan chan interface{}
    stuInterChan = make(chan interface{}, 10)
    stu1 := student{name: "stu01"}
    stuInterChan <- &stu1

    var stu01 interface{}
    stu01 = <-stuInterChan

    fmt.Println(stu01)

    var stu02 *student
    stu02, ok := stu01.(*student)
    if !ok {
        fmt.Println("can not convert")
        return
    }
    fmt.Println(stu02)
}
channel

8. chan之間的同步 

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 < 100000; i++ {
            intChan <- i
        }
        close(intChan)
    }()
    for i := 0; i < 8; i++ {
        go calc(intChan, resultChan, exitChan)
    }
    // 等待全部的groutine所有退出
    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
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() {
    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

9.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)
	}
}

10. chan的關閉

  1. 使用內置函數close進行關閉,chan關閉以後,for range遍歷chan中已經放入的元素

  2. 使用內置函數close進行關閉, chan關閉以後,沒有使用for range的寫法,須要判斷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)
	}
}

11. chan的只讀和只寫

  a. 只讀chan的聲明

var 變量的名字 <-chan int
var readChan <-chan int

  b.只寫chan的聲明

var 變量的名字 chan <- int
var writeChan chan <- int
package main

import (
    "bufio"
    "fmt"
    "io"
    "os"
)

func main() {
    file, err := os.Open("test.log")
    if err != nil {
        fmt.Println(err)
        return
    }
    defer file.Close()
    // 帶緩存區的文件讀寫
    reader := bufio.NewReader(file)
    var line []byte
    for {
        data, prefix, err := reader.ReadLine()
        if err == io.EOF {
            break
        }
        line = append(line, data...)
        if !prefix {
            fmt.Printf("data: %s\n", string(data))
            line = line[:]
        }
    }
    // fmt.Println(line)
}
帶緩存區的讀寫

12.對chan進行select操做

package main

import (
	"fmt"
	"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)
		}
		// var b int
		// b = <-ch
		// fmt.Println(b)
	}
}

13.定時器的使用

package main

import (
	"runtime"
	"time"
)

func main() {
	num := runtime.NumCPU()
	runtime.GOMAXPROCS(num - 1)
	for i := 0; i < 1024; i++ {
		go func() {
			for {
				select {
				case <-time.After(time.Microsecond):
					// fmt.Println("get data timeout")
				}
			}
		}()
	}
	time.Sleep(time.Second * 1000)
}

14.一次定時器、超時控制

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 <-time.After(time.Microsecond):
					fmt.Println("timeout")
				}
				t.Stop()
			}
		}()
	}
	time.Sleep(time.Second * 1000)
}

15.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 < 100; i++ {
        go calc()
    }

    time.Sleep(time.Second * 1000)
}

3、單元測試

  • 1.文件名必須以_test.go結尾

  • 2. 使用Test開頭的函數名做爲測試函數

  • 3. 測試案例

package main

func add(a, b int) int {
    return a + b
}

func sub(a, b int) int {
    return a - b
}
calc.go
package main

import (
    "encoding/json"
    "io/ioutil"
)

type student struct {
    Name string
    Sex  string
    Age  int
}

func (p *student) Save() (err error) {
    data, err := json.Marshal(p)
    if err != nil {
        return
    }
    err = ioutil.WriteFile("stu.dat", data, 0755)
    return
}

func (p *student) Load() (err error) {
    data, err := ioutil.ReadFile("stu.dat")
    if err != nil {
        return
    }
    err = json.Unmarshal(data, p)
    return
}
student.go
package main

import "testing"

func TestAdd(t *testing.T) {
    r := add(2, 4)
    if r != 6 {
        t.Fatalf("add(2,4) error, expect:%d, actual:%d", 6, r)
    }
    t.Log("test add succ")
}
func TestSub(t *testing.T) {
    r := sub(2, 4)
    if r != -2 {
        t.Fatalf("sub(2,4) error, expect:%d, actual:%d", -2, r)
    }
    t.Logf("test sub succ")
}
calc_test.go
package main

import "testing"

func TestSave(t *testing.T) {
    stu := &student{
        Name: "stu01",
        Sex:  "man",
        Age:  10,
    }
    err := stu.Save()
    if err != nil {
        t.Fatalf("save student failed: err%v", err)
    }
}

func TestLoad(t *testing.T) {
    stu := &student{
        Name: "stu01",
        Sex:  "man",
        Age:  10,
    }

    err := stu.Save()
    if err != nil {
        t.Fatalf("save student failed: err%v", err)
    }
    stu2 := &student{}
    err = stu2.Load()
    if err != nil {
        t.Fatalf("load student failed,err: %v", err)
    }

    if stu.Name != stu2.Name {
        t.Fatalf("load student failed, Name not equal")
    }

    if stu.Age != stu2.Age {
        t.Fatalf("load student failed, Age not equal")
    }

    if stu.Sex != stu2.Sex {
        t.Fatalf("load student failed, Sex not equal")
    }
}
student_test.go
package main
main.go
相關文章
相關標籤/搜索