GOLANG并发特性

· · 682 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

使用goroutine

func echo(r io.Reader, w io.Writer) {
    io.Copy(w, r)
}

func main() {
    go echo(os.Stdin, os.Stdout)
    time.Sleep(30 * time.Second)
    fmt.Println("timeout!")
    os.Exit(0)
}

使用goroutine闭包

func main() {
    fmt.Println("main goroutine.")
    // 这里的匿名函数不一定什么时候执行
    go func() {
        fmt.Println("main.func1 goroutine.")
    }()
    fmt.Println("main goroutine again.")
    // 主动让出时间
    runtime.Gosched()
}

等待goroutine

func compress(filename string) error {
    in, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer in.Close()
    out, err := os.Create(filename + ".gz")
    if err != nil {
        return err
    }
    defer out.Close()
    gzout := gzip.NewWriter(out)
    _, err = io.Copy(gzout, in)
    gzout.Close()
    return err
}

func main() {
    var wg sync.WaitGroup

    var i int = -1
    var file string
    for i, file = range os.Args[1:] {
        // 使用wg来等待goroutine
        wg.Add(1)
        // 闭包创建链式调用 匿名函数的参数是外部自由变量 
        // 如果不给参数内部compress将使用file的循环最后的值
        // 一个循环中使用goroutine时 要注意goroutine使用的变量
        // 是否被for循环改变了 最简单的方法就是循环内复制变量
        go func(filename string) {
            compress(filename)
            wg.Done()
        }(file)
    }
    wg.Wait()
    fmt.Printf("Compressed %d files\n", i+1)
}

互斥锁

type words struct {
    // 多个goroutine维护一个map 需要对map加锁
    // TODU:golang bible chapter 9
    sync.Mutex
    found map[string]int
}

func newWords() *words {
    return &words{found: map[string]int{}}
}

func (w *words) add(word string, n int) {
    w.Lock()
    defer w.Unlock()
    count, ok := w.found[word]
    if !ok {
        w.found[word] = n
    }
    w.found[word] = count + n
}

func tallyWords(filename string, dict *words) error {
    file, err := os.Open(filename)
    if err != nil {
        return err
    }
    scanner := bufio.NewScanner(file)
    scanner.Split(bufio.ScanWords)
    for scanner.Scan() {
        word := strings.ToLower(scanner.Text())
        dict.add(word, 1)
    }
    return scanner.Err()
}

func main() {
    var wg sync.WaitGroup
    w := newWords()
    for _, f := range os.Args[1:] {
        wg.Add(1)
        go func(filename string) {
            if err := tallyWords(filename, w); err != nil {
                fmt.Println(err.Error())
            }
            wg.Done()
        }(f)
    }
    wg.Wait()
    fmt.Println("words that appear more than once.")
    w.Lock()
    for word, count := range w.found {
        if count > 1 {
            fmt.Printf("%s:%d\n", word, count)
        }
    }
    w.Unlock()

}

安全的关闭通道:在接收方从通道内拿到所有数据后发送一个done来通知发送方关闭channel

func worker(msg chan string, done chan bool) {
    for {
        select {
        case <-done:
            fmt.Println("Done.")
            close(msg)
            return
        default:
            msg<-"hello world."
            time.Sleep(500*time.Millisecond)
        }
    }
}

func main() {
    msg := make(chan string)
    done := make(chan bool)
    timeout := time.After(5 * time.Second)
    // 将done传入worker 如果main接收完成通知worker关闭msg
    // 如果从worker直接关闭 main会继续接收msg的零值
    // 如果在main直接关闭 worker会向关闭的channel发送引起panic
    go worker(msg, done)

    for {
        select {
        case m := <-msg:
            fmt.Println("recieved msg:", m)
        case <-timeout:
            done <- true
            time.Sleep(500 * time.Millisecond)
            return
        }
    }
}

锁定缓冲通道:将一个通道作为锁来使用

  • 函数通过在channel上发送信息获取锁
  • 函数继续读写等修改共享内存
  • 通过读取channel中的数据释放锁
  • 在该锁被释放之前 是一直被占用状态
func worker(id int, lock chan bool) {
    fmt.Printf("%d wants the lock.\n", id)
    // 谁先向channel发数据这个锁就归谁
    lock <- true
    fmt.Printf("%d has the lock.\n", id)
    time.Sleep(500 * time.Millisecond)
    fmt.Printf("%d is releasing the lock.\n", id)
    // 从该channel中读取数据意味着释放锁
    <-lock
}

func main() {
    lock := make(chan bool, 1)
    for i := 1; i < 7; i++ {
        // 将channel作为锁使用 多个goroutine使用同一个channel
        go worker(i, lock)
    }
    time.Sleep(10 * time.Second)
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:

查看原文:GOLANG并发特性

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

682 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传