Go基础:了解 Goroutines, Channels, Select, Timeouts in Go

让我们深入探索名为“Go 中的 Goroutines、Channels、Select、Timeouts”的令人兴奋的并发世界。了解并发编程,包括 Goroutines 和 Channels。😄

full lessons here👇:
https://programmerscareer.com/zh-cn/golang-basic-skill/

由AI生成,可能有错误,仅供参考

Topic: 并发编程介绍

并发是 Go 的核心概念,它指的是在多个任务同时进行的进度。在计算机编程中,一个并发程序是一个在同一时间内有多个任务正在进行,但不一定是在同时执行的状态。

在 Go 中,并发通过 goroutines 实现,这些 goroutines 是超轻量级进程(或线程)。它们比线程更便宜,并且对开发者的管理被抽象化。Go 运行时包含一个调度器,它协调了大量 goroutines 的执行,并将其在少数线程上执行。

Topic: Goroutines

Goroutine 是 Go 运行时管理的轻量级线程。你可以通过在函数调用前添加关键字 go 来创建一个 goroutine,例如:

1
go funcName()

这将启动 funcName 的执行,并让 Go 调度器将其安排到逻辑处理器上。

让我们来看一个简单的示例:

1
2
3
4
5
6
7
8
9
10
func hello() {
fmt.Println("Hello World!")
}

func main() {
go hello()
fmt.Println("I'm listening…")
// 等待一段时间以便程序不立即退出
time.Sleep(1 * time.Second)
}

我们使用 time 包中的 Sleep 函数来暂停主 goroutine 的执行,以便 hello goroutine 有足够的时间完成输出。

这只是对 goroutines 的初步介绍。它们是 Go 的一个丰富和强大的方面,允许我们编写优雅和高效的并发代码。

Topic: Channels

在 Go 中,Channels 是 goroutines 之间可以通信的管道。创建新的 channel 的语法是 make( chan val-type ),其中 val-type 表示通过 channel 可以发送的数据类型。

以下是一个简单的示例,演示如何通过 channel 发送和接收消息:

1
2
3
4
5
6
7
8
9
10
func main() {
messages := make(chan string)

go func() {
messages <- "Hello, Gopher"
}()

message := <-messages
fmt.Println(message)
}

在这个示例中,我们首先创建一个字符串类型的 channel。然后,我们启动一个 goroutine,它将 “Hello, Gopher” 消息发送到 channel。在主 goroutine 中,我们从 channel 接收消息并打印出来。

注意数据流向箭头方向。

Topic: 缓冲 Channels

默认情况下,Channels 是无缓冲的,这意味着它们只有在有相应的接收器时才会接受发送。缓冲 Channels 可以接受有限数量的值而不需要相应的接收器。

1
2
3
4
5
6
7
8
func main() {
buffChannel := make(chan string, 2)
buffChannel <- "buffered"
buffChannel <- "channel"

fmt.Println(<-buffChannel)
fmt.Println(<-buffChannel)
}

在这个示例中,我们创建了一个缓冲 channel 的大小为 2。由于 channel 是缓冲的,所以我们可以将值发送到 channel 而不需要相应的接收器。

Topic: Select in Go

Go 中的 select 语句允许我们同时处理多个通道。select 会阻塞直到其中的一个 case 可以运行,然后执行该 case。如果有多个 case 都准备好了,它将随机选择一个。

让我们来看一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
func fibonacci(c, quit chan int)  {  
x, y := 0, 1
for {
select {
case c <- x:
x, y = y, x+y
case <-quit:
fmt.Println("quit")
return
}
}
}

func main() {
c := make(chan int)
quit := make(chan int)
go func() {
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
quit <- 0
}()
fibonacci(c, quit)
}

我们创建了两个通道 cquit。然后,我们启动一个 goroutine,发送一些整数到通道 c,然后发送 0 到 quit 通道。fibonacci 函数使用 select,在每次迭代中尝试从通道 cquit 中 case。

Topic: Default Selection and Timeouts

如果 select 中的所有 case 都不可用,那么默认 case 将被执行。这是实现非阻塞 select 语句的方法。

当与 time.After 结合使用时,select 变得是一个非常好的方式来实现超时。time.After 返回一个通道,该通道将在指定的持续时间后发送当前时间。

下面是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main()  {  
c := make(chan string, 1)

go func() {
time.Sleep(2 * time.Second)
c <- "result"
}()

select {
case res := <-c:
fmt.Println(res)
case <-time.After(1 * time.Second):
fmt.Println("timeout")
}

}

在这个程序中,select 等待从通道 c 接收消息。然而,它将只等待 1 秒,因为 time.After(1 * time.Second)。在 1 秒后,time.After 发送当前时间,导致 select 解除阻塞并打印 “timeout”。

Topic: Real World Uses of Concurrency

现在,你可能会问:“Concurrency 在实际世界中被用于哪里?”Concurrency 允许程序同时执行多个任务,这可以提高系统资源的利用率和性能。

  1. Server Applications:Web 和应用服务器使用 concurrency 来管理来自客户端的多个连接。例如,当用户向网站发送请求时,处理该网站流量的服务器使用一个单独的 goroutine 处理每个个体请求。这允许服务器同时处理多个请求,而不需要其他用户等待某个用户的请求被发送或处理。
  2. Data Analysis and Big Data:数据分析通常涉及对大量数据的处理,这可能会花费很长时间如果sequentially 进行。解决方案可以使用 concurrency 来并行处理不同的数据子集,从而减少总体处理时间。
  3. Game Development:在视频游戏中,Concurrency 可以用于处理用户输入、绘制图形、运行 AI 逻辑等所有事情。这使得游戏可以保持平滑的视觉效果和响应的控制,即使在高负载情况下。
  4. Distributed Systems:在分布式和云基于系统中,需要与多个服务器同时通信。Concurrency 可以并行执行这些通信,以提高系统 oversight 和一致性。
  5. Multithreading Operations:类似于服务器应用程序,GUI 基于应用程序也使用 concurrency 来确保用户界面线程永远不阻塞,并提供一个顺滑的用户体验。

正如我们所看到的,Concurrency 的应用场景非常广泛。一次你掌握了它,你将能够编写更加高效和有效的 Go 程序。

English post: https://programmerscareer.com/go-basic-09/
作者:Wesley Wei – Twitter Wesley Wei – Medium
注意:本文为作者原创,转载请注明出处。

Go基础:理解 Reading Files, Writing Files, Line Filters in Go Go基础:了解 File Paths, Directories, Temporary Files and Directories, Embed Directive in Go

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×