Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit bc888e7

Browse files
committed
2 parents c0d3cff + 285447c commit bc888e7

File tree

5 files changed

+228
-0
lines changed

5 files changed

+228
-0
lines changed

‎demo/channel/demo01.go‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,21 @@
11
package main
22

3+
import (
4+
"fmt"
5+
)
6+
37
func main() {
8+
var m map[string]string
9+
if m == nil {
10+
fmt.Println("this is nil map")
11+
}
12+
m = make(map[string]string)
13+
m["name"] = "Tinywan"
14+
fmt.Println(m)
415

16+
m1 := map[string]int{}
17+
fmt.Println(m1)
18+
m1["age"] = 24
19+
m1["dateTime"] = 20180909
20+
fmt.Println(m1)
521
}

‎demo/http_request.go‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"io/ioutil"
6+
"net/http"
7+
)
8+
9+
var (
10+
url1 = "https://www.tinywan.com/api/open/returnUrl"
11+
url2 = "https://restapi.amap.com/v3/weather/weatherInfo?key=d2315f3b0b4e57bbf5428e755a73e692&city=110101"
12+
)
13+
14+
func main() {
15+
res, err := doHttpGetRequest(url2)
16+
if err != nil {
17+
fmt.Println("net req error")
18+
} else {
19+
fmt.Println(res)
20+
}
21+
}
22+
23+
// http get请求函数
24+
// 输入参数:请求url
25+
// 返回值:res,天气数据。err,错误信息
26+
func doHttpGetRequest(url string) (res string, err error) {
27+
28+
// http.Get在net/http中,所以要import "net/http"
29+
resp, err := http.Get(url)
30+
31+
if err != nil {
32+
return "", err
33+
} else {
34+
// 使用efer resp.Body.Close()。当doHttpGetRequest成功return之后,执行此行语句。多用于句柄关闭
35+
defer resp.Body.Close()
36+
37+
// io流数据读取。需要引用io/ioutil
38+
body, err := ioutil.ReadAll(resp.Body)
39+
40+
if err != nil {
41+
return "", err
42+
} else {
43+
return string(body), err
44+
}
45+
}
46+
}

‎demo/slice0090.go‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package main
2+
3+
func main() {
4+
var s []int
5+
s = make([]int, 1)
6+
s[0] = 12
7+
}

‎demo/struct/demo01.go‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
type person struct {
8+
name string
9+
age int
10+
sex bool
11+
}
12+
13+
func (p *person) isSex() {
14+
if p.age > 25 {
15+
p.sex = true
16+
}
17+
18+
if p.sex {
19+
fmt.Println("Who is ", p.name)
20+
}
21+
}
22+
23+
func main() {
24+
p1 := person{name: "Tinywan", age: 24}
25+
p1.isSex()
26+
p2 := person{name: "Shaobo", age: 26}
27+
p2.isSex()
28+
p3 := person{name: "MaLi", age: 32}
29+
p3.isSex()
30+
}

‎docs/golang_tutorial_22.md‎

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
2+
21 - 协程
3+
========================
4+
5+
上一节:[第十六篇 结构体](/docs/golang_tutorial_16.md)
6+
下一节:[第十八篇 接口一](/docs/golang_tutorial_18.md)
7+
8+
这是本Golang系列教程的第21篇。
9+
10+
在上一篇教程中,我们讨论了并发,以及并发和并行的区别。在这篇教程中我们将讨论在Go中如何通过Go协程实现并发。
11+
12+
## 什么是协程?
13+
14+
Go协程(`Goroutine`)是与其他[函数](/docs/golang_tutorial_6.md)[方法](/docs/golang_tutorial_17.md)同时运行的函数或方法。可以认为Go协程是轻量级的线程。与创建线程相比,创建Go协程的成本很小。因此在Go中同时运行上千个协程是很常见的。
15+
16+
## Go协程对比线程的优点
17+
18+
* 与线程相比,Go协程的开销非常小。Go协程的堆栈大小只有几kb,它可以根据应用程序的需要而增长和缩小,而线程必须指定堆栈的大小,并且堆栈的大小是固定的。
19+
* Go协程被多路复用到较少的OS线程。在一个程序中数千个Go协程可能只运行在一个线程中。如果该线程中的任何一个Go协程阻塞(比如等待用户输入),那么Go会创建一个新的OS线程并将其余的Go协程移动到这个新的OS线程。所有这些操作都是 runtime 来完成的,而我们程序员不必关心这些复杂的细节,只需要利用 Go 提供的简洁的 API 来处理并发就可以了。
20+
* Go 协程之间通过信道(`channel`)进行通信。信道可以防止多个协程访问共享内存时发生竟险(race condition)。信道可以想象成多个协程之间通信的管道。我们将在下一篇教程中介绍信道。
21+
22+
## 如何创建一个协程?
23+
24+
在函数或方法调用之前加上关键字 `go`,这样便开启了一个并发的Go协程。
25+
26+
让我们创建一个协程:
27+
```golang
28+
package main
29+
30+
import (
31+
"fmt"
32+
)
33+
34+
func hello() {
35+
fmt.Println("Hello world goroutine")
36+
}
37+
func main() {
38+
go hello()
39+
fmt.Println("main function")
40+
}
41+
```
42+
43+
第11行,`go hello()` 开启了一个新的协程。现在 `hello()` 函数将和 `main()` 函数一起运行。`main` 函数在单独的协程中运行,这个协程称为主协程。
44+
45+
运行这个程序,你将得到一个惊喜。程序仅输出了一行文本: `main function`
46+
47+
**我们创建的协程发生了什么?我们需要了解Go协程的两个属性,以了解为什么发生这种情况。**
48+
49+
* 当创建一个Go协程时,创建这个Go协程的语句立即返回。与函数不同,程序流程不会等待Go协程结束再继续执行。程序流程在开启Go协程后立即返回并开始执行下一行代码,忽略Go协程的任何返回值。
50+
* 在主协程存在时才能运行其他协程,主协程终止则程序终止,其他协程也将终止。
51+
52+
我想你已经知道了为什么我们的协程为什么没有运行。在11行调用 `go hello()`后,程序的流程直接调转到下一条语句执行,并没有等待 `hello` 协程退出,然后打印 `main function`
53+
54+
接着主协程结束运行,不会再执行任何代码,因此 `hello` 协程没有得到运行的机会。
55+
56+
让我们修复这个问题:
57+
58+
```golang
59+
package main
60+
61+
import (
62+
"fmt"
63+
"time"
64+
)
65+
66+
func hello() {
67+
fmt.Println("Hello world goroutine")
68+
}
69+
func main() {
70+
go hello()
71+
time.Sleep(1 * time.Second)
72+
fmt.Println("main function")
73+
}
74+
```
75+
76+
上面的程序中,第13行,我们调用 `time` 包的 `Sleep` 函数来使调用该函数所在的协程休眠。在这里是让主协程休眠1秒钟。现在调用 `go hello()` 有了足够的时间得以在主协程退出之前执行。该程序首先打印 `Hello world goroutine`,等待1秒钟之后打印 `main function`
77+
78+
在主协程中使用 `Sleep` 函数等待其他协程结束的方法是不正规的,我们用在这里只是为了说明**Go协程**是如何工作的。信道可以用于阻塞主协程,直到其他协程执行完毕。我们将在下一篇教程中讨论信道。
79+
80+
## 开启多个协程
81+
82+
让我们写一个程序开启多个协程来更好的理解协程。
83+
84+
```golang
85+
package main
86+
87+
import (
88+
"fmt"
89+
"time"
90+
)
91+
92+
func numbers() {
93+
for i := 1; i <= 5; i++ {
94+
time.Sleep(250 * time.Millisecond)
95+
fmt.Printf("%d ", i)
96+
}
97+
}
98+
func alphabets() {
99+
for i := 'a'; i <= 'e'; i++ {
100+
time.Sleep(400 * time.Millisecond)
101+
fmt.Printf("%c ", i)
102+
}
103+
}
104+
func main() {
105+
go numbers()
106+
go alphabets()
107+
time.Sleep(3000 * time.Millisecond)
108+
fmt.Println("main terminated")
109+
}
110+
```
111+
112+
上面的程序在第21和22行开启了两个协程。现在这两个协程同时执行。`numbers` 协程最初睡眠 `250` 毫秒,然后打印 `1`,接着再次睡眠然后打印`2`,以此类推,直到打印到 `5`。类似地,`alphabets` 协程打印从 `a``e` 的字母,每个字母之间相隔 `400` 毫秒。主协程开启 `numbers``alphabets` 协程,等待 `3000` 毫秒,最后终止。
113+
114+
程序的输出为:
115+
116+
```golang
117+
1 a 2 3 b 4 c 5 d e main terminated
118+
```
119+
120+
下面的图片描述了这个程序是如何工作的,请在新的标签中打开图像以获得更好的效果:)
121+
122+
![Goroutines-explained](../images/Goroutines-explained.png)
123+
124+
上图中,**蓝色**的线框表示 `numbers` 协程,**栗色**的线框表示 `alphabets` 协程。**绿色**的线框表示主协程。**黑色**的线框合并了上述三个协程,向我们展示了该程序的工作原理。每个框顶部的 `0ms`,`250 ms` 的字符串表示以**毫秒**为单位的时间,在每个框底部的 `1`,`2`,`3` 表示输出。
125+
126+
蓝色的线框告诉我们在 250ms 的时候打印了`1`,在 `500ms` 的时候打印了`2`,以此类推。因此最后一个线框底部的输出:`1 a 2 3 b 4 c 5 d e main terminated` 也是整个程序的输出。上面的图像是很好理解的,您将能够了解该程序的工作原理。
127+
128+
Go协程的介绍就到这里。祝你有美好的一天!
129+
希望你喜欢阅读。请留下宝贵的意见和反馈:)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /