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.Mutexsync.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”。通过合理地使用通道和同步机制,可以保证并发程序的正确性和稳定性。