在上一篇文章Go 语言入门指南:基础语法和常用特性解析(上)中,我总结了与Go语言相关的安装和设置、基础语法等知识点,今天的文章中我们将继续介绍Go语言的复合数据类型和并发编程相关的知识~
3. 复合数据类型
在Go语言中,复合数据类型允许你将多个值组合在一起,以创建更为复杂和结构化的数据结构。本部分将介绍Go语言中的几种重要的复合数据类型,包括数组、切片、映射(Map)和结构体(Struct)。
3.1. 数组
- 数组是一种固定长度、相同数据类型元素的集合。
- 声明数组:
var arr [n]T
,其中n
为数组长度,T
为元素类型。 - 初始化数组:
arr := [n]T{val1, val2, ...}
。 - 访问数组元素:使用索引访问,索引从 0 开始。
与多数传统语言一样,Go语言的数组在功能使用上相差不大,唯一注意的是其变量定义遵循Go语言自己的定义风格,即数据类型后置 。同时,使用len()
方法可以获取数组长度。
less
var arr [10]int
arr[0] = 1123
arr[1] = 5813
fmt.Println(arr[1], len(arr)) // 5813 2
3.2. 切片
- 切片是动态长度、指向数组的引用,类似于动态数组。
- 创建切片:
slice := make([]T, length, capacity)
,其中length
为初始长度,capacity
为底层数组容量。 - 切片操作:可以使用
slice[start:end]
对切片进行切割。 - 切片扩容:通过
append
函数将元素添加到切片。
而对于切片,其为Go语言特有的类型,可以类比为动态数组,即声明时不指定大小。
go
arr := make([]int, 3)
arr[0] = 1
arr[1] = 1
arr[2] = 2
fmt.Println(arr[1], len(arr)) // 1 3
//append()追加数据至切片
arr = append(arr, 3)
arr = append(arr, 5, 8)
fmt.Println(arr) // [1 1 2 3 5 8]
//copy()复制切片
arr2 := make([]int, len(arr))
copy(arr2, arr)
fmt.Println(arr2) // [1 1 2 3 5 8]
//类似Python的切片语法
fmt.Println(arr[2:5]) // [2 3 5]
fmt.Println(arr[:5]) // [1 1 2 3 5]
fmt.Println(arr[2:]) // [2 3 5 8]
3.3. 映射
- 创建映射:
m := make(map[K]V)
,其中K
为键的类型,V
为值的类型。 - 插入和访问元素:
m[key] = value
,value = m[key]
。 - 删除元素:
delete(m, key)
。
Map 是一种无序的键值对 的集合,通过 key 来快速检索数据。
go
// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)
// 使用字面量创建 Map
m = map[string]int{
"apple" : 1,
"banana" : 2,
"orange" : 3
}
// 获取键值对
vl := m["apple"]
v2, ok := m["pear"]
// 如果键不存在,ok 的值为 false,v2 的值为该类型的零值
// 修改键值对
m["apple"] = 5
// 获取map长度
len_m := len(m)
// 删除键值对
delete(m, "banana")
3.4. 结构体
- 声明结构体:
type StructName struct { field1 type1; field2 type2; ... }
。 - 创建结构体实例:
instance := StructName { field1: val1, field2: val2, ... }
。 - 访问结构体字段:使用
instance.field
。
与其他的语言类似,Go语言中的结构体仍然是不同数据类型的字段的集合,用于表示一个更复杂的数据结构,其用法和其他语言相差不大。
go
type Books struct {
tilte string
author string
subject string
bookid int
}
// 声明 book1 为 Books 类型
var book1 Books
// book1 各参数赋值
book1.title = "Hello, Go!"
book1.author = "张三"
book1.subject = "IT"
book1.bookid = 123456
4. 并发编程
并发是指在同一时间内处理多个任务的能力,Go语言以其独特的并发模型在编写并发程序时具有明显优势。本部分将深入介绍Go语言中的并发编程,包括Goroutine、通道(Channel)和并发模式等内容。
4.1. Goroutine
- Goroutine 是Go语言的轻量级线程,由Go运行时管理。
- 使用关键字
go
启动一个Goroutine,将函数调用包装为并发任务。 - Goroutine 之间共享内存,但通常通过通道进行通信,避免竞态条件。
Goroutine 是 Go 语言中的轻量级并发执行单元,是其并发模型的核心组成部分。Goroutine 使得在同一个程序中同时执行多个任务变得更加容易和高效,而且在语言层面上提供了内置的支持。
Goroutine 的创建和销毁开销非常小,相较于操作系统线程来说,创建数千甚至数百万个 Goroutine 是可行的。
同时,在 Go 程序中,你可以同时启动多个 Goroutine,它们将会并发地执行,不需要手动管理线程池或任务队列。
以下是一个使用 Goroutine 的简单示例,计算 1 到 10 的阶乘并打印结果:
go
package main
import (
"fmt"
)
func factorial(n int, ch chan int) {
result := 1
for i := 1; i <= n; i++ {
result *= i
}
ch <- result
}
func main() {
ch := make(chan int)
for i := 1; i <= 10; i++ {
go factorial(i, ch)
}
for i := 1; i <= 10; i++ {
result := <-ch
fmt.Printf("Factorial of %d: %d\n", i, result)
}
}
通过使用 Goroutine,Go 语言能够以高效、简洁的方式实现并发,充分利用多核处理器的能力,从而提升程序性能和响应能力。然而,需要注意正确地处理 Goroutine 之间的同步和通信,以避免竞态条件和数据访问问题。
4.2. 通道(Channel)
- 通道是Goroutine之间进行通信和同步的机制。
- 使用
make
函数创建通道:ch := make(chan T)
,T
为通道中传递的数据类型。 - 发送数据到通道:
ch <- data
。发送操作会阻塞,直到通道有足够的空间来存储数据。 - 从通道接收数据:
data := <-ch
。接收操作会阻塞,直到通道中有数据可供接收。 - 通道也支持带缓冲的通道:
ch := make(chan T, capacity)
。
通道(Channel) 是 Go 语言中用于实现并发安全的数据传递和通信的机制。通道可以在 Goroutine 之间传递数据,并且能够在数据传递的同时进行同步操作,从而有效地避免了竞态条件和其他并发问题。
默认情况下,通道的发送和接收操作都是阻塞的,直到有一方准备好。
通过使用 select
语句可以实现非阻塞的通道操作,从而避免 Goroutine 阻塞在通道操作上。
通道适用于多个 Goroutine 之间的数据传递和同步,比如任务队列、消息传递等场景。
以下是一个简单的示例,演示了如何使用通道来计算两个数的和:
go
package main
import (
"fmt"
)
func add(a, b int, ch chan int) {
sum := a + b
ch <- sum
}
func main() {
ch := make(chan int)
go add(3, 5, ch)
result := <-ch
fmt.Println("Result:", result)
}
4.3. 并发模式
- 基于通道的并发模式:使用通道在Goroutine之间传递数据,实现数据同步和任务协调。
- 扇出-扇入模式:将一个输入通道分发到多个处理Goroutine,然后将结果合并到一个输出通道。
- 选择器模式:使用
select
语句监听多个通道,响应可读或可写的通道操作。 - 互斥锁和条件变量:通过
sync
包实现对共享资源的互斥访问和条件同步。 - 工作池模式:工作池维护一组 Goroutine,用于执行特定的任务。
- 串行模式:使用通道将多个 Goroutine 串行化,确保它们按顺序执行。
并发模式是一组在并发编程中常用的设计模式,用于解决在多个并发执行的 Goroutine 之间协调、同步和通信的问题。这些模式帮助开发者编写出更可靠、高效的并发程序,避免竞态条件、死锁和其他并发问题。
这些并发模式帮助开发者解决了在并发编程中常见的问题,使得并发程序更易于理解、维护和调试。根据不同的应用场景,开发者可以选择合适的模式来组织并发代码,从而充分利用多核处理器的能力,提高程序性能和响应能力。