Go语言中的并发程序可以用两种手段来实现:goroutine 和 channel,其支持顺序通信进程,或被简称为CSP,CSP是一种并发编程模型,在这种并发编程模型中,值会在不同运行实例中传递,第二个手段便是多线程共享内存
网络编程是并发最适合的领域之一,最典型的是服务器要同时处理很多连接程序,我们来写个顺序执行的时钟服务器,让它每隔1秒钟将当前时间写到客户端
package main
import (
"io"
"log"
"net"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
handleConn(conn)
}
}
func handleConn(c net.Conn) {
defer c.Close()
for {
_, err := io.WriteString(c, time.Now().Format("15:04:05\n"))
if err != nil {
return // e.g., client disconnected
}
time.Sleep(1 * time.Second)
}
}
我们创建了一个listener来监听网络端口到来的连接,然后用handleConn来处理这个完整的客户端连接,我们运行一下
xxx@MacBook-Pro-10 ~ % nc localhost 8000
21:27:58
21:27:59
21:28:00
21:28:01
21:28:02
21:28:03
客户端将服务器发来的时间显示了出来,我们也可像下面这样改写一下代码
package main
import (
"io"
"log"
"net"
"os"
)
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
mustCopy(os.Stdout, conn)
}
func mustCopy(dst io.Writer, src io.Reader) {
if _, err := io.Copy(dst, src); err != nil {
log.Fatal(err)
}
}
我们现在同时在两个终端中运行测试,我们看到智能在一个终端中执行,也就是说第二个客户段必须等待第一个客户端完成才能进行
xxx@MacBook-Pro-10 ~ % ./netcat1
22:51:22
22:51:23
22:51:24
22:51:25
22:51:26
22:51:27
22:51:28
22:51:29
22:51:30
^C
xxx@MacBook-Pro-10 ~ % ./netcat1
22:52:20
22:52:21
22:52:22
22:52:23
22:52:24
22:52:25
22:52:26
22:52:27
^C
$ killall clock1
killall 命令是Unix命令行工具,可以杀掉指定名称的进程
现在我们把程序使用go关键字改成并发支持
for {
conn,err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
现在我们在多个终端中运行,它可以同时运行了
$ go build gopl.io/ch8/clock2
$ ./clock2 &
$ go build gopl.io/ch8/netcat1
$ ./netcat1
14:02:54 $ ./netcat1
14:02:55 14:02:55
14:02:56 14:02:56
14:02:57 ^C
14:02:58
14:02:59 $ ./netcat1
14:03:00 14:03:00
14:03:01 14:03:01
^C 14:03:02
^C
$ killall clock2