为 Ruby 程序员准备的 Go 入门教程(2)

如果这样继续下去的话,文章就可能成为Go语言语法的的简明教程。而我更希望将时间花费在一些有意思的新特性上,而不是这样的一个语法介绍。基本语法可以参照Go语言的文档,那会介绍得更好。

下面让我们看看goroutines吧。

Goroutines

写并发的代码已经很困难了,写并发访问网络的代码就更加困难了。问题在于传统的线程不能很好得伸缩,而且线程一旦运行起来,就会很难去控制。Go语言项目组着手解决这个问题,于是乎goroutine就诞生了。

本质上, goroutines是个轻量级的并发机制,通过使用一种称为channels的构建来进行线程间交互。它们都非常易于使用:

package main
 
import "fmt"
 
func wait() {
  //wait around with a forever loop
  for {
  }
}
 
func main() {
  go wait()
  fmt.Println("We didn't wait because it was called as a goroutine!")
}

在上面的代码中,wait方法是一个死循环,但是我们通过go wait()的方式来调用,而非直接的通过wait()来调用。这是告诉Go我们希望以一个goroutine的方式来调用,同时异步运行。既然这个循环是在后台运行的,那样运行这个程序就不会因为死循环而阻塞。

这么说,Go从语言本身支持并发。也就是,Go语言中有并发基元(primitives)。这样意义何在呢?仅仅因为不是由某个库或者模块来实现并发,这好像不是什么了不起的举措啊。但是,实际上goroutine从根本上与线程不同。goroutine更加轻量化。还记得在服务器中,我们不该为每个客户端创建一个线程吧?但是,使用goroutine,情况就不同了:

package main
 
import (
  "fmt"
  "net"
)
 
//notice that in the arguments, the name of
//the variable comes first, then comes the
//type of the variable, just like in "var"
//declarations
func manageClient(conn net.Conn) {
  conn.Write([]byte("Hi!"))
  conn.Close()
  //do something with the client
}
 
func main() {
  //we are creating a server her that listens
  //on port 1337. Notice that, similar to Ruby,
  //a method can have two return values (although
  //in Ruby, this would be an array instead)
  listener, err := net.Listen("tcp", ":1337")
  for {
    //accept a connection
    connection, _ := listener.Accept()
    go manageClient(connection)
  }
}

噢,等会!这些代码似有那么一小点复杂啊,虽然想法是很简单。好吧,让我们一步一步慢慢来

首先,我们来看一下main函数。在main函数一开始调用了net.Listen方法,该方法会返回两个值,一个是服务器连接,另一个是错误消息。然后,进入到服务的主循环部分,在这儿程序调用server.Accept方法,然后等待请求。该方法调用后,程序会被挂起,直到有有一个客户端的连接出现。一旦有个连接出现,我们将connection对象传值到manageClient方法中,由于通过goroutine的方式调用manageClient,所以主程序会继续等待处理下一个客户端连接请求。

最后,关于这个manageClient方法要注意一下。首先,注意一下参数表,是变量名在先,类型在后。这样的格式多少是由Go语言创造者决定的。你可能甚至可能一周后都没有注意到。

在方法体中,向客户端写入“Hi!”信息,然后关闭套接字。

好了,就这么几行代码,我们轻松完成了一个基础服务器。你可以将它改成一个HTTP代理(如果加上缓存,那就更棒了)。Goroutines支持我们这么做。事实上goroutine不单单是一个轻量级的线程,因为还有许多与众不同的机制在背后在起着作用,所以才可以通过如此简练的代码的来实现goroutine功能。

Channels

虽然,单纯只有Goroutines已经很有作用了,但是如果在channels概念的支持下,那么Goroutines将更具威力。Channels是一种goroutine之间或者goroutine和主进程之间的通信机制。让我们来看个简单的实例。

package main
 
import (
  "fmt"
)
 
var eventChannel chan int = make(chan int)
 
func sayHello() {
  fmt.Println("Hello, world!")
 
  //pass a message through the eventChannel
  //it doesn't matter *what* we actually send across
  eventChannel < - 1
}
 
func main() {
 
  //run a goroutine that says hello
  go sayHello()
 
  //read the eventChannel
  //this call blocks so it waits until sayHello()
  //is done
  <- eventChannel
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/73b419566f948c5df0024576b366fb36.html