并发编程

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

Go 语言简介(下)— 特性

并发不是并行

Go负载均衡

一个Go例程(Goroutines)就是一个和其它Go例程在同一地址空间里但却独立运行的函数,就像是在shell里使用 & 标记启动一个命令。

Goroutine 的并发安全性

无论是 Windows 还是 Linux,基本上来说是用操作系统的线程来实现的,不过,Goroutine不是线程。它有个特性,如果当一个Go例程阻塞时,所在的线程会阻塞,但其它Go例程不受影响,多个例程可以在系统线程上做多路通信。堆栈初始很小,但随着需求会增长或收缩,Go例程不是不耗资源,但它们很轻量级的。
这并不是真正的并发,如果你要真正的并发,你需要在你的 main 函数的第一行加上下面的这段代码:

import "runtime"
...
runtime.GOMAXPROCS (4)

并发(Concurrency)不是并行(Parallelism)

--Rob Pike
Go语言提供:

  • 并发执行(goroutines)
    并发是一种将一个程序分解成小片段独立执行的程序设计方法,通信是指各个独立的执行任务间的合作。
    并发将相互独立的执行过程综合到一起的编程技术。(这里是指通常意义上的执行过程,而不是Linux进程。很难定义。)
    并行同时执行(通常是相关的)计算任务的编程技术。
    并发是指同时处理很多事情,而并行是指同时能完成很多事情,两者不同,但相关,一个重点是组合,一个重点是执行。
    并发提供了一种方式让我们能够设计一种方案将问题(非必须的)并行的解决。
  • 同步和消息传输(channels)
  • 多路并发控制(select)

通道 Channels

通道是类型化的值,能够被Go例程用来做同步或交互信息。

timerChan := make(chan time.Time)
go func() {
    time.Sleep(deltaT)
    timerChan <- time.Now() // send time on timerChan
}()
// Do something else; when ready, receive.
// Receive will block until timerChan delivers.
// Value sent is other goroutine's completion time.
completedAt := <-timerChan

Select

这select语句很像switch,但它的判断条件是基于通信,而不是基于值的等量匹配。

select {
case v := <-ch1:
    fmt.Println("channel 1 sends", v)
case v := <-ch2:
    fmt.Println("channel 2 sends", v)
default: // optional
    fmt.Println("neither channel was ready")
}

闭包

它让一些并发运算更容易表达:

func Compose(f, g func(x float) float)
                  func(x float) float {
     return func(x float) float {
        return f(g(x))
    }
}
print(Compose(sin, cos)(0.5))

Demo

// 使用闭包封装一个后台操作:
go func() { // 从输入通道拷贝数据到输出通道
    for val := range input {
        output <- val
    }
}()//这个for range操作会一直执行到处理掉通道内最后一个值。

// 数据类型:
type Work struct {
    x, y, z int
}

// 一个worker的任务:
func worker(in <-chan *Work, out chan<- *Work) {
   for w := range in {
      w.z = w.x * w.y
      Sleep(w.z)
      out <- w
   }
}//必须保证当一个worker阻塞时其他worker仍能运行。

// runner:
func Run() {
   in, out := make(chan *Work), make(chan *Work)
   for i := 0; i < NumWorkers; i++ {
       go worker(in, out)
   }
   go sendLotsOfWork(in)
   receiveLotsOfResults(out)
} // 很简单的任务,但如果没有并发机制,你仍然很难这么简单的解决。

这个负载均衡的例子具有很明显的并行和可扩展性,Worker数可以非常巨大。Go语言的这种并发特征能的开发一个安全的、好用的、可扩展的、并行的软件变得很容易。
并发简化了同步,没有明显的需要同步的操作,程序的这种设计隐含的实现了同步。

Demo(查询数据库)

func Query(conns []Conn, query string) Result {
    ch := make(chan Result, len(conns))  // buffered
    for _, conn := range conns {
        go func(c Conn) {
            ch <- c.DoQuery(query):
        }(conn)
    }
    return <-ch
}

并发和垃圾回收机制让这成为一个很小很容易解决的问题。


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

本文来自:简书

感谢作者:紫若丹枫

查看原文:并发编程

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

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