Go语言之多线程

前言

在 Go 语言中,虽然常用术语是 goroutine,但实际上 Go 本身并不是使用传统的多线程来实现并发,而是使用了一种称为”m:n”调度的机制。

这意味着在一些 OS 线程上运行多个 goroutine,以提高并发性能。

多线程

下面是一个使用 Go 实现多线程的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
"fmt"
"sync"
)

func main() {
var wg sync.WaitGroup

for i := 0; i < 5; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()

fmt.Printf("Goroutine %d started\n", id)
for j := 1; j < 10; j++ {
fmt.Printf("Goroutine %d counting: %d\n", id, j)
}
fmt.Printf("Goroutine %d finished\n", id)
}(i)
}

wg.Wait()
}

该代码使用 Go 中的协程(Goroutine)实现多线程。我们创建了一个 sync.WaitGroup 对象来跟踪所有协程的状态。然后,我们使用 go 关键字来为每个协程创建一个新的协程。在协程的执行体中,我们输出一些信息,然后进行计数操作。在计数完成后,我们输出一些信息,表示协程已经完成。最后,我们使用 Wait 方法,等待所有协程完成。

与 Rust 类似,Go 也是一种并发安全的语言,并且在多线程编程时需要注意避免数据竞争等问题。Go 提供了 sync.Mutex、sync.RWMutex 等机制来解决这些问题。在需要时,可以使用这些机制来保护共享数据的访问。

需要注意的是,Go 的协程机制与线程不同,它是由 Go 运行时(Go Runtime)管理的,可以轻松实现大规模并发,同时避免了线程的创建和销毁带来的开销。

WaitGroup

为了确保两个 goroutine 都执行完毕后再打印 “OK”,可以使用通道(channel)来实现 goroutine 间的同步。

以下是一个示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
"fmt"
"sync"
)

func sayHello(wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; i < 5; i++ {
fmt.Println("Hello")
}
}

func main() {
var wg sync.WaitGroup
wg.Add(2)

go sayHello(&wg)
go sayHello(&wg)

wg.Wait()

fmt.Println("OK")
}

在上面的示例中,通过传入一个 sync.WaitGroup 对象来实现两个 goroutine 的同步。

sync.WaitGroup 可以在 goroutine 中使用 Add 方法增加计数,使用 Done 方法减少计数,而 Wait 方法可以阻塞主 goroutine 直到计数减为 0。

在 sayHello 函数中,我们通过 defer wg.Done() 让每个 goroutine 在完成任务后减少计数。

在 main 函数中,我们启动两个 goroutine 后等待它们都完成后再打印 “OK”。

这样就可以确保两个 goroutine 都执行完毕后再打印 “OK”。通过合理地使用通道和同步机制,可以保证并发程序的正确性和稳定性。