mirror of
https://github.com/gopl-zh/gopl-zh.github.com.git
synced 2026-01-16 12:17:13 +08:00
rebuild
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<title>示例: 併發的Echo服務 | Go编程语言</title>
|
||||
<title>示例: 併發的Echo服務 | Go语言圣经</title>
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="description" content="">
|
||||
<meta name="generator" content="GitBook 2.5.2">
|
||||
@@ -48,7 +48,7 @@
|
||||
<body>
|
||||
|
||||
|
||||
<div class="book" data-level="8.3" data-chapter-title="示例: 併發的Echo服務" data-filepath="ch8/ch8-03.md" data-basepath=".." data-revision="Mon Dec 28 2015 16:03:52 GMT+0800 (中国标准时间)">
|
||||
<div class="book" data-level="8.3" data-chapter-title="示例: 併發的Echo服務" data-filepath="ch8/ch8-03.md" data-basepath=".." data-revision="Thu Dec 31 2015 16:18:40 GMT+0800 (中国标准时间)">
|
||||
|
||||
|
||||
<div class="book-summary">
|
||||
@@ -575,7 +575,7 @@
|
||||
|
||||
<b>4.2.</b>
|
||||
|
||||
切片
|
||||
Slice
|
||||
</a>
|
||||
|
||||
|
||||
@@ -590,7 +590,7 @@
|
||||
|
||||
<b>4.3.</b>
|
||||
|
||||
字典
|
||||
Map
|
||||
</a>
|
||||
|
||||
|
||||
@@ -2013,7 +2013,7 @@
|
||||
<!-- Title -->
|
||||
<h1>
|
||||
<i class="fa fa-circle-o-notch fa-spin"></i>
|
||||
<a href="../" >Go编程语言</a>
|
||||
<a href="../" >Go语言圣经</a>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -2024,7 +2024,91 @@
|
||||
<section class="normal" id="section-">
|
||||
|
||||
<h2 id="83-示例-併發的echo服務">8.3. 示例: 併發的Echo服務</h2>
|
||||
<p>TODO</p>
|
||||
<p>clock服務器每一個連接都會起一個goroutine。在本節中我們會創建一個echo服務器,這個服務在每個連接中會有多個goroutine。大多數echo服務僅僅會返迴他們讀取到的內容,就像下面這個簡單的handleConn函數所做的一樣:</p>
|
||||
<pre><code class="lang-go"><span class="hljs-keyword">func</span> handleConn(c net.Conn) {
|
||||
io.Copy(c, c) <span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring errors</span>
|
||||
c.Close()
|
||||
}
|
||||
</code></pre>
|
||||
<p>一個更有意思的echo服務應該模擬一個實際的echo的“迴響”,併且一開始要用大寫HELLO來表示“聲音很大”,之後經過一小段延遲返迴一個有所緩和的Hello,然後一個全小寫字母的hello表示聲音漸漸變小直至消失,像下面這個版本的handleConn(譯註:笑看作者腦洞大開):</p>
|
||||
<pre><code class="lang-go">gopl.io/ch8/reverb1
|
||||
<span class="hljs-keyword">func</span> echo(c net.Conn, shout <span class="hljs-typename">string</span>, delay time.Duration) {
|
||||
fmt.Fprintln(c, <span class="hljs-string">"\t"</span>, strings.ToUpper(shout))
|
||||
time.Sleep(delay)
|
||||
fmt.Fprintln(c, <span class="hljs-string">"\t"</span>, shout)
|
||||
time.Sleep(delay)
|
||||
fmt.Fprintln(c, <span class="hljs-string">"\t"</span>, strings.ToLower(shout))
|
||||
}
|
||||
|
||||
<span class="hljs-keyword">func</span> handleConn(c net.Conn) {
|
||||
input := bufio.NewScanner(c)
|
||||
<span class="hljs-keyword">for</span> input.Scan() {
|
||||
echo(c, input.Text(), <span class="hljs-number">1</span>*time.Second)
|
||||
}
|
||||
<span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring potential errors from input.Err()</span>
|
||||
c.Close()
|
||||
}
|
||||
</code></pre>
|
||||
<p>我們需要陞級我們的客戶端程序,這樣它就可以發送終端的輸入到服務器,併把服務端的返迴輸出到終端上,這使我們有了使用併發的另一個好機會:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch8/netcat2
|
||||
<span class="hljs-keyword">func</span> main() {
|
||||
conn, err := net.Dial(<span class="hljs-string">"tcp"</span>, <span class="hljs-string">"localhost:8000"</span>)
|
||||
<span class="hljs-keyword">if</span> err != <span class="hljs-constant">nil</span> {
|
||||
log.Fatal(err)
|
||||
}
|
||||
<span class="hljs-keyword">defer</span> conn.Close()
|
||||
<span class="hljs-keyword">go</span> mustCopy(os.Stdout, conn)
|
||||
mustCopy(conn, os.Stdin)
|
||||
}
|
||||
</code></pre>
|
||||
<p>當main goroutine從標準輸入流中讀取內容併將其發送給服務器時,另一個goroutine會讀取併打印服務端的響應。當main goroutine碰到輸入終止時,例如,用戶在終端中按了Control-D(^D),在windows上是Control-Z,這時程序就會被終止,盡管其它goroutine中還有進行中的任務。(在8.4.1中引入了channels後我們會明白如何讓程序等待兩邊都結束)。</p>
|
||||
<p>下面這個會話中,客戶端的輸入是左對齊的,服務端的響應會用縮進來區别顯示。
|
||||
客戶端會向服務器“喊三次話”:</p>
|
||||
<pre><code>$ go build gopl.io/ch8/reverb1
|
||||
$ ./reverb1 &
|
||||
$ go build gopl.io/ch8/netcat2
|
||||
$ ./netcat2
|
||||
Hello?
|
||||
HELLO?
|
||||
Hello?
|
||||
hello?
|
||||
Is there anybody there?
|
||||
IS THERE ANYBODY THERE?
|
||||
Yooo-hooo!
|
||||
Is there anybody there?
|
||||
is there anybody there?
|
||||
YOOO-HOOO!
|
||||
Yooo-hooo!
|
||||
yooo-hooo!
|
||||
^D
|
||||
$ killall reverb1
|
||||
</code></pre><p>註意客戶端的第三次shout在前一個shout處理完成之前一直沒有被處理,這貌似看起來不是特别“現實”。眞實世界里的迴響應該是會由三次shout的迴聲組合而成的。爲了模擬眞實世界的迴響,我們需要更多的goroutine來做這件事情。這樣我們就再一次地需要go這個關鍵詞了,這次我們用它來調用echo:</p>
|
||||
<pre><code class="lang-go">gopl.io/ch8/reverb2
|
||||
<span class="hljs-keyword">func</span> handleConn(c net.Conn) {
|
||||
input := bufio.NewScanner(c)
|
||||
<span class="hljs-keyword">for</span> input.Scan() {
|
||||
<span class="hljs-keyword">go</span> echo(c, input.Text(), <span class="hljs-number">1</span>*time.Second)
|
||||
}
|
||||
<span class="hljs-comment">// <span class="hljs-doctag">NOTE:</span> ignoring potential errors from input.Err()</span>
|
||||
c.Close()
|
||||
}
|
||||
</code></pre>
|
||||
<p>go後跟的函數的參數會在go語句自身執行時被求值;因此input.Text()會在main goroutine中被求值。
|
||||
現在迴響是併發併且會按時間來覆蓋掉其它響應了:</p>
|
||||
<pre><code>$ go build gopl.io/ch8/reverb2
|
||||
$ ./reverb2 &
|
||||
$ ./netcat2
|
||||
Is there anybody there?
|
||||
IS THERE ANYBODY THERE?
|
||||
Yooo-hooo!
|
||||
Is there anybody there?
|
||||
YOOO-HOOO!
|
||||
is there anybody there?
|
||||
Yooo-hooo!
|
||||
yooo-hooo!
|
||||
^D
|
||||
$ killall reverb2
|
||||
</code></pre><p>讓服務使用併發不隻是處理多個客戶端的請求,甚至在處理單個連接時也可能會用到,就像我們上面的兩個go關鍵詞的用法。然而在我們使用go關鍵詞的同時,需要慎重地考慮net.Conn中的方法在併發地調用時是否安全,事實上對於大多數類型來説也確實不安全。我們會在下一章中詳細地探討併發安全性。</p>
|
||||
|
||||
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user