前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Go 语言社区】并发性

【Go 语言社区】并发性

作者头像
李海彬
发布2018-03-20 12:02:21
7480
发布2018-03-20 12:02:21
举报
文章被收录于专栏:Golang语言社区Golang语言社区

并发性

并发的特点是需要锁Lock和互斥Mutex。在Java中加锁和解锁是一个复杂过程代码如下:

代码语言:javascript
复制
try {
  mutex.acquire();
  try {
    // do something
  } finally {
    mutex.release();
  }
} catch(InterruptedException ie) {
  // ...
}

并发另外一个特性是异步,各种语言都有自己的异步机制,基于回调的有:

  1. Ruby的 EventMachine
  2. Python的 Twisted
  3. NodeJS

但是不能很好地与并行共处,依赖各种库包,代码难于调试,易陷入回调嵌套地狱。见callbackhell.com

Go的并发基于两个概念:

  • 协程goroutine: 是一种轻量线程,它不是操作系统的线程,而是将一个操作系统线程分段使用,通过调度器实现协作式调度。是一种绿色线程,微线程。
  • 通道channel: 类似Unix的Pipe,用于协程之间通讯和同步。协程之间虽然解耦,但是它们和Channel有着耦合。

比如sleep和taalk代码如下:

代码语言:javascript
复制
func sleepAndTalk(t time.Duration, msg string) {
    time.Sleep(t)
    fmt.Printf("%v ", msg)
}

每秒一个消息:

代码语言:javascript
复制
func main() {
    sleepAndTalk(0*time.Second, "Hello")
    sleepAndTalk(1*time.Second, "Gophers!")
    sleepAndTalk(2*time.Second, "What's")
    sleepAndTalk(3*time.Second, "up?")
}
如果我们不是每秒,而是需要同时发送消息呢?加上go即可:
func main() {
    go sleepAndTalk(0*time.Second, "Hello")
    go sleepAndTalk(1*time.Second, "Gophers!")
    go sleepAndTalk(2*time.Second, "What's")
    go sleepAndTalk(3*time.Second, "up?")
}

这是main开启一个主协程,当其结束整个程序也就结束。

下面我们看看通过Channel进行通讯,这时sleepAndTalk 就不是打印出信息,而是将字符串发送给channel了。

代码语言:javascript
复制
func sleepAndTalk(secs time.Duration, msg string, c chan string) {
    time.Sleep(secs * time.Second)
    c <- msg
}

我们创建channel然后将其传递给sleepAndTalk, 之后就可以等待数据值发送到channel了:

代码语言:javascript
复制
func main() {
    c := make(chan string)
    go sleepAndTalk(0, "Hello", c)
    go sleepAndTalk(1, "Gophers!", c)
    go sleepAndTalk(2, "What's", c)
    go sleepAndTalk(3, "up?", c)
    for i := 0; i < 4; i++ {
        fmt.Printf("%v ", <-c)
    }
}

下面看看如何在Web环境中实现:首先我们从Channel中接受到nextId:

代码语言:javascript
复制
var nextID = make(chan int)
func handler(w http.ResponseWriter, q *http.Request) {
    fmt.Fprintf(w, "<h1>You got %v<h1>", <-nextID)
}
需要一个协程发送nextID到channel中。
func main() {
    http.HandleFunc("/next", handler)
    go func() {
        for i := 0; ; i++ {
            nextID <- i
        }
    }()
    http.ListenAndServe("localhost:8080", nil)
}

通过浏览器访问localhost:8080/next 可得到nextID数值。

如果有多个Channel,那么代码如下:

代码语言:javascript
复制
var battle = make(chan string)
func handler(w http.ResponseWriter, q *http.Request) {
    select {
    case battle <- q.FormValue("usr"):
        fmt.Fprintf(w, "You won!")
    case won := <-battle:
        fmt.Fprintf(w, "You lost, %v is better than you", won)
    }
}

这样访问的URL参数不同:

Go - localhost:8080/fight?usr=go Java - localhost:8080/fight?usr=java

多个Channel可以串联组成流:

gophers链:

代码语言:javascript
复制
func f(left, right chan int) {
    left <- 1 + <-right
}
func main() {
    start := time.Now()
    const n = 1000
    leftmost := make(chan int)
    right := leftmost
    left := leftmost
    for i := 0; i < n; i++ {
        right = make(chan int)
        go f(left, right)
        left = right
    }
    go func(c chan int) { c <- 0 }(right)
    fmt.Println(<-leftmost, time.Since(start))
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 并发性
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档