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
相关文章
相关标签/搜索