Day8.channel_goroutine

一.goroutine

(一).进程和线程的区别

a.进程是程序在操作系统中的一次执⾏过程,系统进⾏资源分配和调度的一个独立单位。
b.线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是⽐进程更小的能独⽴运行的基本单位。
c.一个进程可以创建和撤销多个线程;同⼀个进程中的多个线程之间可以并发执⾏.

(二).并发和并⾏

a.多线程程序在⼀个核的cpu上运⾏,就是并发. 
b.多线程程序在多个核的cpu上运行,就是并⾏.

并发和并行的区别

(三).协程和线程

a.协程:独立的栈空间,共享堆空间,调度由用户自己控制,本质上有点类似于用户级线程,这些用户级线程的调度也是自己实现的。
b.线程:一个线程上可以跑多个协程,协程是轻量级的线程。

(四).goroutine调度模型

1.调度模型简单示例

M(物理线程) 
P(资源) 
G(goroutine)

goroutine调度模型

2.资源阻塞后简单示例

如果G0打开一个很耗时操作阻塞,它会把物理线程剥离出来,新起一个线程,继续执行Goroutine擦做。

goroutine调度模型

3.设置golang程序运行的核心数。

package main

import "runtime"

func main() {
    num := runtime.NumCPU()
    runtime.GOMAXPROCS(num - 3)

    for i := 0; i < num; i++ {
        go func() {
            for {

            }
        }()
    }

    for {

    }

}

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

1.全局变量和锁同步.  
2.channel.
(1).全局变量写法
package main

import (
    "fmt"
    "time"
)

var exits [3]bool

func calc(index int) {
    for i := 0; i < 1000; i++ {
        time.Sleep(time.Millisecond)
    }
    exits[index] = true
}

func main() {

    //runtime.GOMAXPROCS(4)
    start := time.Now().UnixNano()
    go calc(0)
    go calc(1)
    go calc(2)

    for {
        if exits[0] && exits[1] && exits[2] {
            break
        }
        time.Sleep(time.Millisecond)
    }
    end := time.Now().UnixNano()
    fmt.Printf("finished,cost %d ms", (end-start)/1000/100)
}
(2).锁waitGroup使用案例
package main

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

var waitGroup sync.WaitGroup

func calc(index int) {
    for i := 0; i < 1000; i++ {
        time.Sleep(time.Millisecond)
    }
    waitGroup.Done() // 执行完毕 计数-1
}

func main() {

    //runtime.GOMAXPROCS(4)
    start := time.Now().UnixNano()
    for i := 0; i < 3; i++ {
        waitGroup.Add(1) // 每次开始执行 计数+1
        go calc(i)
    }

    waitGroup.Wait() // 等counter=0,返回结果。
    end := time.Now().UnixNano()
    fmt.Printf("finished,cost %d ms", (end-start)/1000/100)
}

二.channel

(一).channel定义

a.类似Unix中的管道(pipe)
b.先进先出
c.线程安全,多个goroutine同时访问,不需要加锁。
d.channel是有类型的,一个整数的channel只能存放整数。
var initChan chan int = make(chan int, 0) //0表示没有缓冲区
channel statement: var 变量名 chan 类型
var test chain int 
var test chan string 
var test chan map[string]string
var test chan stu
var test chan *stu

(二).channel初始化:

1.使用make进行初始化
var test chan int 
test = make(chan int,10)

var test chan string
test=make(chan string,10)
// 带缓冲区和不带缓冲区测试对比
package main

import (
    "fmt"
    "time"
)

func test() {
    var initChan chan int = make(chan int, 1)
    go func() {
        initChan <- 10
    }()

    result := <-initChan
    fmt.Printf("result:%d\n", result)
}

func testNoBufChan() {
    var initChan chan int = make(chan int, 0)
    go func() {
        fmt.Println("begin input to chan\n")
        initChan <- 10
        fmt.Println("end input to chan\n")
    }()

    result := <-initChan
    fmt.Printf("result:%d\n", result)
    time.Sleep(10 * time.Second)
}

func main() {
    testNoBufChan()
}

(三).channel基本操作

a.从channel读取数据到变量
var testChan chan ini
testChan = make(chan int,10)
var a int
a = <- testChan

b.把数据从变量写入channel
var testChan chan int
testChan = make(chan int,10)
var a int = 10
testChan <- a

(四).goroutine && channel相结合实战案例

package main

import (
    "fmt"
    "sync"
)

var waitGroup sync.WaitGroup

func produce(ch chan string) {
    ch <- "h1"
    ch <- "h2"
    ch <- "h3"
    ch <- "h4"
    ch <- "h5"
    close(ch)
    waitGroup.Done()

}

func consume(ch chan string) {
    for {
        str, ok := <-ch
        if !ok {
            fmt.Println("channel is closed")
            break
        }
        fmt.Printf("value:%s\n", str)
    }
    waitGroup.Done()
}

func main() {
    var ch chan string = make(chan string)
    waitGroup.Add(2) //這裏不能亂寫 否則會拋出異常 產生死鎖。
    go produce(ch)
    go consume(ch)
    waitGroup.Wait()
}

(五).信号量处理案例

package main

import (
    "fmt"
    "os"
    "os/signal"
    "sync"
    "syscall"
)

var waitGroup sync.WaitGroup

func produce(ch chan string, exitChan chan bool) {
    var i int
    var exit bool
    for {
        str := fmt.Sprintf("hello %d", i)
        select {
        case ch <- str:
        case exit := <-exitChan:
        }

        if exit {
            break
        }
    }

    close(ch)
    waitGroup.Done()

}

func consume(ch chan string) {
    for {
        str, ok := <-ch
        if !ok {
            fmt.Println("channel is closed")
            break
        }
        fmt.Printf("value:%s\n", str)
    }
    waitGroup.Done()
}

func main() {
    var ch chan string = make(chan string)
    var exitChan chan bool = make(chan bool, 1)
    var signChan chan os.Signal = make(chan os.Signal, 1)
    waitGroup.Add(2)
    signal.Notify(signChan, syscall.SIGUSR2) // 系统无法找到此语法块。暂时有问题。

    go produce(ch, exitChan)
    go consume(ch)
    <-signChan
    exitChan <- true
    waitGroup.Wait()
}

(六).带缓冲区和不带缓冲区channel

a.不带缓冲区的
var testChan chan int
testChan = make(chan int)
var a int
a = <- testChan

b.带缓冲区的
var testChan chan int
testChan = make(chan int,10)
var a int = 10
testChan <- a

(七).chan的关闭

a.使用内置函数close进行关闭,chan关闭之后,for range遍历chan中已存在的元素后结束。
b.使用内置函数close进行关闭,chan关闭之后,没有使用for range的写法需要使用,v,ok := <- ch进行判断chan是否关闭。

(八).chan的只读和只写

a.只读chan的声明:
var varName <- chan int
var readChan <- chan int

b.只写chan的声明
var varName chan <- int
var writeChan chan <- int

(九).select

select{
    case u:= <- ch1:
    case e := <- ch2:
    default:
}

三.定时器的使用

(一).使用案例

package main

import (
    "fmt"
    "time"
)

func main() {
    t := time.NewTicker(time.Second)
    //cmd := exec.Command("/bin/bash", "-c", "uptime")
    for v := range t.C {
        fmt.Printf("system load is :%v\n", v)

    }
}

(二).oneTicker

package main

import (
    "fmt"
    "time"
)

func main() {
    select {
    case <-time.After(time.Second):
        fmt.Println("After")
    }
}

(三).超时控制例子

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

四.goroutine中使用recove

应用场景,如果某个goroutine panic了,而且这个goroutine里面没有捕获(recover),
那么整个进程就会挂掉。所以,好的习惯是每当go产生一个goroutine,就需要写下recover.
// 捕获异常示例
package main

import (
    "fmt"
    "time"
)

// 注意位置
func calc() {
    defer func() {
        err := recover()
        if err != nil {
            fmt.Println(err)
        }
    }()

    var p *int
    *p = 100
}

func main() {
    go calc()
    time.Sleep(time.Second * 3)
    fmt.Println("progress exited")
}

五.单元测试

1.文件名必须以_test.go结尾
2.使用go test执行单元测试

calc.go

// code
package test

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

func sub(a int, b int) int {
    return a - b
}

calc_test.go // 必须格式calc_test.go

// unitTest code
package test

import "testing"

func TestAdd(t *testing.T) { // 必须以TestXss开头
    result := add(1, 99)
    if result != 100 {
        t.Fatal("add func is not right")
        return
    }
    t.Logf("add is right")
}

func TestSub(t *testing.T) {
    result := sub(1000, 900)
    if result != 1000 {
        t.Fatal("sub is not right")
        return
    }
    t.Logf("sub is right")
}
a. not 非
{{ if not .condition}}
{{end}}

b. and 与
{{ if and .condition1 .condition2 }}
{{end}}

c. or 或
{{ if or .condition1 .condition2 }}
{{end}}

d. eq 等于
{{ if eq .var1 .var2 }}
{{end}}

e. ne 不等于
{{ if ne .var1 .var2}}
{{end}}

f. lt 小于(less than)
{{ if lt .var1 .var2}}
{{end}}

g. le 小于等于
{{ if le .var1 .var2}}
{{end}}

h. gt 大于
{{ if gt .var1 .var2}}
{{end}}

h. ge 大于等于
{{ if ge .var1 .var2}}
{{end}}
  • index.html
<html>
    <head></head>
    <body>
        {{if gt .Age 18}}
        <p>Hello,old man,{{.Name}}</p>
        <p>Hello,old man,{{.}}</p> 
        <!- 如果是. 打印出的结果将是对象本身 ,{tom 23} ->
        {{else}}
        <p>Hello,young man, {{.Name}}</p>
        {{end}}

        <!- 表示嵌套的结构体 ->
        {{with .Name}}
        <p>{{.}}</p>
        {{end}}
    </body>
</html>
  • index2.html
<html>
    <head></head>
    <body>
        <table border="1px">
        {{range .}}
        <tr>
            <td>{{.Name}}</td>
            <td>{{.Age}}</td>
            <td><input type="submit" value="修改"/></td>
        </tr>
        {{end}}
        </table>
    </body>
</html>
  • main.go
package main

import (
    "fmt"
    "net/http"
    "text/template"
)

type Person struct {
    Name string
    Age  int
}

var (
    globalTemp *template.Template
)

func init() {
    t, err := template.ParseFiles(`D:\project\src\godev2\day10\template/index2.html`)
    if err != nil {
        fmt.Println("parse file err:", err)
        return
    }
    globalTemp = t

}

func handleUserInfo(w http.ResponseWriter, r *http.Request) {
    /*
        p := &Person{
            Name: "tom",
            Age:  23,
        }
        globalTemp.Execute(w, p)
    */
    var persons []*Person
    for i := 0; i < 10; i++ {
        p := &Person{
            Name: fmt.Sprintf("stu%d", i),
            Age:  i * 10,
        }
        persons = append(persons, p)
    }
    globalTemp.Execute(w, persons)
}

func main() {

    http.HandleFunc("/user_info", handleUserInfo)
    http.ListenAndServe("0.0.0.0:80", nil)
}
Copyright © zhangluya.com 2019            UPDATE 2020-03-26 15:42:33

results matching ""

    No results matching ""