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

一、Goroute

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

  •   B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。

  •   C. 一个进程可以创建和撤销多个线程:同一个进程中的多个线程之间可以并发执行。

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

二、Channel

1.不同的goroutine之间如何进行通讯?

  a. 全局变量和锁同步

  b. Channel

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

三、单元测试

  • 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

 

转载于:https://www.cnblogs.com/zhangyafei/p/10732795.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值