go

作者: 小小爱笑 | 来源:发表于2019-03-09 15:19 被阅读0次

https://golang.google.cn/

goroutine简单示例

package main

import (
    "fmt"
)

func main() {
    ch := make(chan string)
    for i := 0; i < 50000; i++ {
        go PrintHelloWorld(i, ch)
    }

    for {
        msg := <-ch
        fmt.Println(msg)
    }
}

func PrintHelloWorld(i int, ch chan string) {
    for {
        ch <- fmt.Sprintf("hello world from %d\n", i)
    }
}

goroutine将结果通过chan 传给 主goroutine

当chan可能关闭的时候 接收方需要加入显示判断 , chan关闭时,返回第二个参数false

for {
        if num, ok := <-p; ok {
            fmt.Println(num)
        } else {
            break
        }
    }

接收chan 通过range简写, 当chan close之后range结束。 chan没有close则一直循环。

for v := range p {
        fmt.Println(v)
    }

声明chan参数 或返回值时 ,可以指明 chan的方向。

func ArraySource(a ...int) <-chan int {
    out := make(chan int)
    go func() {
        for _, v := range a {
            out <- v
        }
        close(out)
    }()

    return out
}

<- chan 代表只能从chan中取, 对于使用者而言。

io接口 Reader Writer

文件实现了io接口

file, err = os.Open(filename)
buffer := make([]byte, 8)
n, err := file.Read(buffer)

从文件中读取8个字节

file, err := os.Create(filename)
buffer := make([]byte, 8)
binary.BigEndian.PutUint64(buffer, uint64(v))
file.Write(buffer)

创建文件,写入一个int, 64位上为8字节

缓冲io

使用bufio包装,可以得到带缓存的writer reader

带缓存的writer最终要进行flush()

writer := bufio.NewWriter(file)
 ... 
writer.Flush()

并发模型

将任务分解为多个子任务并发执行

func Run() {
    lines := readlines()
    var wg sync.WaitGroup
    wg.Add(len(lines))
    ch := make(chan string)

    for _, line := range lines {
        go func(l string) {
            newLine := strings.ToUpper(l)
            ch <- newLine
            wg.Done()
        }(line)
    }

    go func() {
        wg.Wait()
        close(ch)
    }()

    Display(ch)
}

func Display(ch chan string) {
    fmt.Println("Display result *** ")
    for c := range ch {
        fmt.Printf(c)
    }
    fmt.Println("*****")
}


向chan中传入内容 回阻塞当前goroutine,直到其他goroutine到chan中获取

ch := make(chan string)
ch <- "result123"              // blocking

从已经close的chan 中获取,会返回 空值。 可以通过返回的第二个参数判断 chan是否close


问题记录:

select 两个chan 的结果
使用select以先到先服务的方式,接收通道发送的消息

    ch1 := doWork1()
    ch2 := doWork2()

    var ok1, ok2 bool
    var r1, r2 string
    for {
        select {
        case r1, ok1 = <-ch1:
            fmt.Println(r1, ok1)
        case r2, ok2 = <-ch2:
            fmt.Println(r2, ok2)
        }
        if ok1 && ok2 {
            break
        }
    }

select 中 ok1 := 导致每次select作用域中新建了ok1变量,select作用域外的ok1变量一直为false, 导致for select无法退出,最终死锁。

ch1 := doWork1()
    ch2 := doWork2()
    var ok1, ok2 bool
    for {
        select {
        case r1, ok1 := <-ch1:
            fmt.Println(r1, ok1)
        case r2, ok2 := <-ch2:
            fmt.Println(r2, ok2)
        }
        if ok1 && ok2 {
            break
        }
    }

⚠️ select也会匹配已经关闭的chan。

ok1, ok2 默认是true, 当两个chan都关闭时为false, 退出for select循环。

var msg string
    ok1, ok2 := true, true

    for ok1 || ok2 {
        select {
        case msg, ok1 = <-c1:
            if ok1 {
                fmt.Printf("%s receive from c1\n", msg)
            }
            fmt.Println("case 1 ", ok1)
        case msg, ok2 = <-c2:
            if ok2 {
                fmt.Printf("%s receive from c2\n", msg)
            }
            fmt.Println("case 2.. ", ok2)
        }
        fmt.Println(ok1, ok2)
    }

int 转 string

使用 strconv.Itoa(u.Id)

string(10) 的结果不是字符串10, 而是10的ascii码字符


json.Marshal失败

json解析的字段 首字母必须为大写,否则解析错误


局部变量覆盖返回值变量

func scanFile(path string) (err error) {
    if content, err := ioutil.ReadFile(path); err == nil {
        name := filepath.Base(path)
        log.Println("scan file : ", name)
        b := blog.Blog{User_Id: 1, Title: strings.Split(name, ".")[0], Content: string(content)}
        b.Create()
    } else {
        log.Println(err)
    }

    log.Println(err)

    return
}

scanFile函数 if := 后有两个err变量 ,返回值的err变量一直为nil
将 := 修改为 = , 保证不会在子作用域下 新建一个err变量。

func scanFile(path string) (err error) {
    var content []byte
    if content, err = ioutil.ReadFile(path); err == nil {
        name := filepath.Base(path)
        log.Println("scan file : ", name)
        b := blog.Blog{User_Id: 1, Title: strings.Split(name, ".")[0], Content: string(content)}
        b.Create()
    }

    return
}

局部变量覆盖入参变量

入参被map的返回值覆盖,导致一个空的对象被加到map中。后续调用时会产生异常。

var sessions map[string]func() Session

func init() {
    sessions = make(map[string]func() Session)
}

func Register(key string, creator func() Session) error {
    if creator, ok := sessions[key]; ok {
        return errors.New("dumplicate key " + key)
    } else {
        sessions[key] = creator
        return nil
    }
}

if creator, ok := sessions[key]; ok {
return errors.New("dumplicate key " + key)
} else {
}

中的第一个返回值 只能在 ok的分支中使用,不能在else中使用,会是一个无效值
只判断map中是否存在,应该忽略第一个返回值。

if _, ok := sessions[key]; ok {
        return errors.New("dumplicate key " + key)
} else {
}

函数中入参包含 切片时,并且函数中进行append操作

入参 需要传入切片的 指针,才能保证函数返回后,切片改变。

go test 缓存

默认情况 当代码没有变化时,再次运行go test会使用缓存结果,不会实际执行。
关闭缓存可以使用 -count=1

相关文章

网友评论

      本文标题:go

      本文链接:https://www.haomeiwen.com/subject/mkgspqtx.html