Golang教程第25篇(并发)

Go 并发

Go语言通过goroutine和channel提供了轻量级的并发编程模型,极大地简化了并发编程的复杂性,提高了程序的执行效率。
goroutine

  • goroutine是Go语言中的轻量级线程实现,由Go运行时(runtime)管理。
  • Go程序会智能地将goroutine中的任务合理地分配给每个CPU。
  • 创建一个goroutine非常简单,只需在函数调用前加上go关键字即可。
  • Goroutine的启动开销非常小,数万个goroutine几乎不会消耗太多系统资源。
  • 使用go关键字,例如“go myFunction()”,即可启动一个goroutine。

channel

  • Channel用于goroutine之间的通信,提供了一种安全的方式来传递数据。
  • 使用channel可以避免共享内存的复杂性和潜在的竞争问题。
  • Channel是进程内的通信方式,通过channel传递对象的过程和调用函数时的参数传递行为比较一致,例如也可以传递指针等。
  • Channel是类型相关的,一个channel只能传递一种类型的值,这个类型需要在声明channel时指定。

Go并发编程的优势

  • 充分利用硬件资源:通过并发和并行,Go语言可以更好地利用多核处理器,提高程序的性能。
  • 提高程序吞吐量:并发允许程序的不同部分同时执行,可以更充分地利用处理器的等待时间,提高程序的吞吐量。
  • 保持应用程序响应性:在图形用户界面(GUI)应用程序中,通过在后台执行任务,可以保持应用程序的响应性。
  • 简化程序结构:使用并发可以简化程序结构,将复杂任务拆分为更小的子任务,然后并发地执行这些子任务,使代码更容易理解和维护。

Goroutine

1.Goroutine的概念
Goroutine是Go语言特有的并发执行体,它允许在相同的地址空间中并行地执行多个函数或方法。与传统的线程相比,Goroutine的创建和销毁代价要小得多,并且其调度是独立于线程的。这使得在单个系统线程上运行成千上万个Goroutine成为可能。

2.Goroutine的特点

  1. 轻量级:Goroutine的内存开销非常小,通常只需要2KB左右,而线程的内存开销则要大得多,通常需要1MB。
    非抢占式调度:Goroutine是自愿让出CPU的,通过协作完成任务切换。这意味着调度是用户态的,而不是由操作系统内核直接管理的。
  2. 并发与并行:Goroutine提供了并发模型,使得多个任务可以同时进行。即使某个Goroutine被阻塞(例如等待I/O操作),Go语言的调度器也会调度其他可执行的Goroutine运行。
  3. 阻塞模型简化开发:Goroutine避免了复杂的回调函数,使得代码结构更加简洁。同时,通过通道(Channel)进行通信和同步,可以进一步简化并发编程的复杂性。

3.Goroutine的创建与启动
要启动一个新的Goroutine,只需在函数调用前加上go关键字。例如:

package main
import (
	"fmt"
)
func sayHello() {
	fmt.Println("Hello, Goroutine!")
}
func main() {
	go sayHello() // 启动一个新的Goroutine来运行sayHello函数
	// 主程序继续执行其他操作...
}

在上面的例子中,sayHello函数被放在一个新的Goroutine中运行。主程序不会等待sayHello函数执行完成,而是会继续执行下去。

4.Goroutine的通信与同步
Goroutine之间通过通道(Channel)进行通信和同步。通道是一种用于在Goroutine之间传递数据的数据结构,它提供了一种安全的方式来实现Goroutine之间的通信。

  1. 创建通道:使用make函数可以创建一个通道。例如,ch := make(chan int)创建了一个整型通道。
  2. 发送数据:使用<-运算符可以向通道发送数据。例如,ch <- 42将整数42发送到通道ch中。
  3. 接收数据:同样使用<-运算符可以从通道接收数据。例如,value := <-ch从通道ch中接收一个整数并将其赋值给变量value。
    通道可以是带缓冲的或无缓冲的。带缓冲的通道在满之前可以缓存一定数量的元素,这使得发送和接收操作变得非阻塞。无缓冲的通道在发送和接收数据时,发送方和接收方必须同时准备好,否则它们将阻塞等待。

5.Goroutine的注意事项

  1. 主Goroutine与子Goroutine的关系:主Goroutine(即main函数所在的Goroutine)结束时,所有未完成的子Goroutine也会被强制终止。因此,在编写并发程序时,需要确保主Goroutine在所有子Goroutine完成之后再结束。
  2. 避免死锁:当多个Goroutine等待彼此时可能出现死锁。为了避免死锁,应小心处理共享资源,并确保每个Goroutine都能在某个时刻继续执行。
  3. 合理使用WaitGroup和Channel:sync.WaitGroup和Channel是Go语言中用于管理多个Goroutine同步和通信的重要工具。合理使用它们可以确保程序的正确性和高效性。

6.Goroutine的应用场景
Goroutine非常适合用于以下场景:

  1. I/O并发:处理大量网络请求、文件读写等I/O操作时,可以使用Goroutine来提高程序的响应时间和吞吐量。
  2. 后台任务:执行定时任务、日志记录等后台任务时,可以使用Goroutine来确保这些任务不会阻塞主程序的执行。
  3. 并行计算:将计算任务分布到多个Goroutine中执行,以提高程序的性能。
    总之,Goroutine是Go语言中实现并发编程的强大工具。通过合理使用Goroutine和通道等并发原语,可以编写出高效、简洁的并发程序。

Channel

1.Channel的基本概念

  • 类型:Channel具有类型,这意味着你可以创建一个只能传递特定类型数据的channel。例如,chan
    int是一个整型channel,chan string是一个字符串型channel。
  • 方向:虽然channel在声明时并不区分发送(send)和接收(receive)方向,但在实际使用中,你可以将channel视为一个单向的通信管道。这意味着你可以有一个只发送数据的channel,一个只接收数据的channel,或者一个既可以发送又可以接收数据的channel。
  • 容量:Channel可以是有缓冲的(buffered)或无缓冲的(unbuffered)。有缓冲的channel可以存储一定数量的元素,在达到容量上限之前,发送操作不会阻塞。无缓冲的channel没有存储空间,因此发送操作会立即阻塞,直到另一方准备好接收数据。

2.Channel的创建
使用make函数可以创建一个新的channel。例如:

ch := make(chan int) // 创建一个无缓冲的整型channel
bufferedCh := make(chan int, 10) // 创建一个容量为10的有缓冲整型channel

3.Channel的使用

  1. 发送数据:使用<-运算符可以将数据发送到channel中。例如,ch <- 42表示将整数42发送到channel ch中。
  2. 接收数据:同样使用<-运算符可以从channel中接收数据。例如,value := <-ch表示从channel
    ch中接收一个数据并将其赋值给变量value。
  3. 关闭Channel:使用close函数可以关闭一个channel。关闭后的channel不能再发送数据,但仍然可以接收数据(直到channel为空)。接收方可以通过检测语法v,
    ok := <-ch来判断channel是否已关闭,其中ok为false表示channel已关闭。

4.Channel的特性

  1. 阻塞:无缓冲的channel在发送和接收时会阻塞。发送操作会阻塞直到另一方准备好接收数据,接收操作会阻塞直到有数据可以接收。有缓冲的channel在达到容量上限时会阻塞发送操作,在缓冲区为空时会阻塞接收操作。
  2. 同步:Channel提供了一种同步机制,使得Goroutine之间可以按照特定的顺序进行通信和协作。
  3. 选择(Select)语句:Go语言中的select语句允许一个Goroutine等待多个channel操作。这使得Goroutine可以根据哪个channel准备好进行通信来做出决策。

5.Channel的应用场景

  1. 生产者-消费者模式:Channel非常适合用于实现生产者-消费者模式。生产者Goroutine将数据发送到channel中,消费者Goroutine从channel中接收数据并处理。
  2. 广播和通知:Channel可以用于在Goroutine之间广播事件或发送通知。例如,一个Goroutine可以在完成某个任务后通过channel通知其他Goroutine。
  3. 并行计算:在并行计算中,可以将任务分割成多个子任务,每个子任务由一个Goroutine执行,并通过channel传递中间结果和最终结果。

总之,Go语言中的channel是一种强大的并发编程工具,它提供了简单而有效的机制来实现Goroutine之间的通信和同步。通过合理使用channel,可以编写出高效、健壮的并发程序。


实例

在Go语言中,并发编程是非常强大的特性,主要通过Goroutine和Channel来实现。下面是一个简单的示例,演示了如何使用Goroutine和Channel进行并发编程。
这个示例将创建一个简单的生产者-消费者模型,其中生产者将整数发送到Channel,而消费者将从Channel中接收这些整数并打印出来。

package main
import (
	"fmt"
	"sync"
	"time"
)
 
// 生产者函数,发送整数到Channel
func producer(ch chan<- int, wg *sync.WaitGroup, id int) {
	defer wg.Done()
	for i := 0; i < 5; i++ {
		fmt.Printf("Producer %d produced: %d\n", id, i)
		ch <- i
		time.Sleep(time.Millisecond * 500) // 模拟生产时间
	}
	close(ch) // 生产完成后关闭Channel
}
 
// 消费者函数,从Channel接收整数并打印
func consumer(ch <-chan int, wg *sync.WaitGroup, id int) {
	defer wg.Done()
	for num := range ch {
		fmt.Printf("Consumer %d consumed: %d\n", id, num)
		time.Sleep(time.Millisecond * 750) // 模拟消费时间
	}
}
 
func main() {
	var wg sync.WaitGroup
 
	// 创建一个无缓冲的Channel
	ch := make(chan int)
 
	// 启动两个生产者
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go producer(ch, &wg, i)
	}
 
	// 启动两个消费者
	for i := 1; i <= 2; i++ {
		wg.Add(1)
		go consumer(ch, &wg, i)
	}
 
	// 等待所有Goroutine完成
	wg.Wait()
	fmt.Println("All tasks are done.")
}

1.代码说明
producer函数:
生产者函数接收一个发送整数类型的Channel和一个WaitGroup指针。
使用for循环发送5个整数到Channel,每次发送后暂停500毫秒以模拟生产时间。
生产完成后关闭Channel。

consumer函数:
消费者函数接收一个接收整数类型的Channel和一个WaitGroup指针。
使用for range循环从Channel中接收整数并打印,每次接收后暂停750毫秒以模拟消费时间。

main函数:
创建一个无缓冲的Channel。
启动两个生产者Goroutine和两个消费者Goroutine。
使用WaitGroup等待所有Goroutine完成。

2.运行结果
运行该程序时,你会看到生产者和消费者交替打印消息,表明它们并发地运行。最终,当所有任务完成时,程序会打印"All tasks are done."。

3.注意事项

  • Channel的关闭:在生产者中关闭Channel是很重要的,这样消费者可以检测到Channel何时没有更多的数据。
  • WaitGroup:确保所有Goroutine在main函数结束前完成。
  • 缓冲Channel:上面的示例使用了无缓冲Channel,你可以根据需要创建带缓冲的Channel来存储更多的数据。
<think>嗯,用户想要一个从零开始的Golang语言教程。首先,我需要确定用户的基础水平,可能是一个完全没有编程经验的新手,或者有其他语言经验但想学Go的人。不管怎样,教程需要从基础开始,逐步深入。 首先,应该介绍Go的背景,比如由Google开发,强调简洁、高效和并发处理。然后,安装和环境搭建是必须的,用户需要知道如何设置Go的工作环境,包括GOPATH和GOROOT这些概念,可能还需要指导如何安装VS Code或其他编辑器。 接下来,基本语法结构。第一个程序通常是“Hello, World!”,这能帮助用户快速上手。然后介绍变量声明、数据类型、控制结构如if-else、for循环,可能还要提到Go没有while循环,而是用for实现。函数定义也是基础部分,包括多返回值的特性。 然后,进阶部分应该包括指针、结构体、方法、接口,这些是Go面向对象的核心概念。还有数组、切片、映射这些数据结构,以及错误处理机制,比如error类型和panic/recover。 并发是Go的亮点,需要详细讲解goroutine和channel,通过例子展示如何实现并发通信。包管理和模块部分,现在Go Modules已经是标准,需要解释go mod的使用,以及如何导入第三方包。 工具链部分,介绍go build、go run、go test等命令,还有测试和性能分析工具如pprof。最后,实战项目建议,比如CLI工具、Web服务,或者并发应用,帮助用户巩固知识。 用户可能还关心学习资源,比如官方文档、在线教程、书籍推荐,以及社区支持。需要确保内容结构清晰,步骤明确,避免信息过载。同时,注意示例代码的正确性和简洁性,让用户容易理解和练习。可能还需要提醒常见的陷阱和最佳实践,帮助用户避免错误。</think>以下是一份从零开始的 Go 语言(Golang)基础教程大纲,适合编程新手或转语言的学习者: --- ### **一、Go 语言简介** - **特点**:简洁、高效、并发支持、编译型语言 - **应用场景**:后端服务、云原生(Docker/K8s)、微服务、CLI工具 - **优势**:快速编译、垃圾回收、原生并发模型(goroutine) --- ### **二、环境搭建** 1. **安装** - 官网下载:[https://golang.org](https://go.dev/) - 验证安装:终端输入 `go version` 2. **配置环境变量** - `GOPATH`(工作目录) - `GOROOT`(Go安装路径) 3. **编辑器推荐** - VS Code(安装 Go 插件) - Goland(JetBrains 专业 IDE) --- ### **三、基础语法** #### 1. 第一个程序 ```go package main import "fmt" func main() { fmt.Println("Hello, World!") } ``` - `go run hello.go` 直接运行 #### 2. 变量与数据类型 ```go var name string = "Alice" // 显式声明 age := 25 // 类型推断(短声明) const PI = 3.14 // 常量 // 基础类型:int, float64, bool, string // 复合类型:array, slice, map, struct ``` #### 3. 控制结构 ```go // if-else if x > 10 { fmt.Println("Large") } else { fmt.Println("Small") } // for 循环(无 while) for i := 0; i < 5; i++ { ... } // switch switch day { case "Mon": fmt.Println("周一") default: fmt.Println("其他") } ``` #### 4. 函数 ```go func add(a int, b int) int { return a + b } // 多返回值 func swap(x, y string) (string, string) { return y, x } ``` --- ### **四、进阶特性** #### 1. 指针与结构体 ```go type Person struct { Name string Age int } p := Person{Name: "Bob"} p.Age = 30 ``` #### 2. 接口与错误处理 ```go type Shape interface { Area() float64 } // 错误处理 result, err := someFunction() if err != nil { log.Fatal(err) } ``` #### 3. 并发编程 ```go // goroutine(轻量级线程) go func() { fmt.Println("Running in goroutine") }() // channel 通信 ch := make(chan string) go func() { ch <- "message" }() msg := <-ch ``` --- ### **五、包管理与工具链** - **Go Modules**(依赖管理) ```bash go mod init project-name go get github.com/package-name ``` - **常用命令** - `go build`:编译 - `go test`:运行测试 - `go fmt`:格式化代码 --- ### **六、学习资源推荐** 1. **官方文档**:[https://go.dev/doc/](https://go.dev/doc/) 2. **在线教程**: - [Tour of Go](https://go.dev/tour/)(交互式学习) - 《Go 语言圣经》(中文版) 3. **书籍**: - 《Go 程序设计语言》 - 《Go Web 编程》 --- ### **七、实战项目建议** 1. 编写命令行工具(如文件管理器) 2. 构建 RESTful API(使用 Gin 或 Echo 框架) 3. 实现并发网络爬虫 通过以上步骤,你可以逐步掌握 Go 语言的核心特性。建议边学边写代码,注重理解并发模型和接口设计!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值