Skip to content

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)