【Go 语言社区】并发性

并发性

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

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代码如下:

func sleepAndTalk(t time.Duration, msg string) {
    time.Sleep(t)
    fmt.Printf("%v ", msg)
}

每秒一个消息:

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了。

func sleepAndTalk(secs time.Duration, msg string, c chan string) {
    time.Sleep(secs * time.Second)
    c <- msg
}

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

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:

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,那么代码如下:

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链:

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

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2016-03-05

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏软件开发

前端MVC Vue2学习总结(二)——Vue的实例、生命周期与Vue脚手架(vue-cli)

一、Vue的实例 1.1、创建一个 Vue 的实例 每个 Vue 应用都是通过 Vue 函数创建一个新的 Vue 实例开始的: var vm = new Vue...

5077
来自专栏LanceToBigData

JavaWeb(五)之JSTL标签库

前言   前面介绍了EL表达式,其实EL表达式基本上是和JSTL核心标签库搭配一起使用才能发挥效果的。接下来让我们一起来认识一下吧!   在之前我们学过在JSP...

27410
来自专栏Golang语言社区

Go Channel 详解

Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication)。

2122
来自专栏北京马哥教育

python实现简单爬虫功能

iOS开发如果之前没接触过除了c和c++(c++太难了,不花个十来年基本不可能精通)的语言,第二门语言最好的选择就是Python.原因就是 1.语法简单 2.库...

3207
来自专栏帘卷西风的专栏

linux下shell技巧

    经常看到一些大牛操作linux的时候,双手运指如飞,指令如流水般输出,会不会感到羡慕呢?

781
来自专栏柠檬先生

Angularjs基础(六)

AngularJS HTML DOM     AngularJS为HTML DOM 元素的属性提供了绑定应用数据的指令。 ng-disabled指令    ...

2068
来自专栏python3

tkinter -- CheckButton

可以表示两种状态:On 和 Off,可以设置回调函数,每当点击此按钮时回调函数被调用

871
来自专栏前端儿

深入理解JavaScript的事件循环(Event Loop)

在两个环境下的Event Loop实现是不一样的,在浏览器中基于 规范 来实现,不同浏览器可能有小小区别。在Node中基于 libuv 这个库来实现

1222
来自专栏有趣的django

python爬虫入门(三)XPATH和BeautifulSoup4

 XML和XPATH 用正则处理HTML文档很麻烦,我们可以先将 HTML文件 转换成 XML文档,然后用 XPath 查找 HTML 节点或元素。 XML 指...

3384
来自专栏逸鹏说道

Python3 与 C# 并发编程之~ 进程篇中

接着上面继续拓展,补充说说获取函数返回值。 上面是通过成功后的回调函数来获取返回值,这次说说自带的方法:

1333

扫码关注云+社区

领取腾讯云代金券