Golang并发模型怎么使用
在Go语言中,并发是编程的核心概念之一,Go语言提供了多种并发模型,包括goroutine、channel、select语句等,本文将详细介绍Go语言的并发模型及其使用方法。
goroutine
goroutine是Go语言中最简单的并发模型,它是由Go语言运行时管理的轻量级线程,可以在一个程序中创建大量的goroutine来实现并发,创建一个goroutine非常简单,只需在函数调用前加上关键字go
即可。
package main import ( "fmt" "time" ) func printNumbers() { for i := 0; i < 10; i++ { time.Sleep(1 * time.Second) fmt.Printf("%d ", i) } } func printLetters() { for i := 'A'; i < 'A'+10; i++ { time.Sleep(1 * time.Second) fmt.Printf("%c ", i) } } func main() { go printNumbers() // 开启一个新的goroutine执行printNumbers函数 go printLetters() // 开启一个新的goroutine执行printLetters函数 time.Sleep(20 * time.Second) // 主线程等待20秒以确保所有子线程执行完毕 }
channel
channel是Go语言中用于在不同goroutine之间传递数据的通道,它可以看作是一个无界的队列,允许多个goroutine向其中发送数据,或从其中接收数据,channel的使用需要遵循一定的规则,如发送和接收操作必须在不同的goroutine中进行,以下是一个简单的channel使用示例:
package main import ( "fmt" "time" ) func producer(ch chan int) { for i := 0; i < 10; i++ { ch <i // 将数据发送到channel中 time.Sleep(1 * time.Second) } close(ch) // 关闭channel,表示没有更多的数据发送了 } func consumer(ch chan int) { for data := range ch { // 从channel中接收数据 fmt.Printf("%d ", data) time.Sleep(1 * time.Second) } } func main() { ch := make(chan int) // 创建一个channel go producer(ch) // 在一个新的goroutine中执行producer函数,并将channel传递给它 go consumer(ch) // 在另一个新的goroutine中执行consumer函数,并将channel传递给它 time.Sleep(20 * time.Second) // 主线程等待20秒以确保所有子线程执行完毕 }
select语句
select语句是Go语言中用于同时处理多个channel操作的一种机制,当有多个channel操作可发生时,select语句会随机选择其中一个操作执行,如果没有操作可发生,select语句会阻塞当前goroutine直到有操作可发生为止,以下是一个简单的select语句使用示例:
package main import ( "fmt" "time" ) func printer(ch1 chan string, ch2 chan string) { for data := range ch1 { // 从ch1 channel接收数据并打印出来 select { // 当ch2 channel中有数据可读时,从ch2 channel中读取数据并打印出来,否则继续等待ch2 channel中有数据可读的情况发生(阻塞当前goroutine) case msg := <-ch2: // 从ch2 channel中接收数据并打印出来(非阻塞) default: // 如果没有其他操作可发生,继续等待(阻塞当前goroutine) time.Sleep(500 * time.Millisecond) // 每隔500毫秒检查一次是否有其他操作可发生(避免过度占用CPU资源) } fmt.Println(data) // 打印接收到的数据(非阻塞) } }
相关问题与解答
1、如何使用Go语言实现多任务调度?可以使用goroutine来实现多任务调度,每个goroutine代表一个独立的任务,通过控制goroutine的启动和停止,可以实现对任务的调度和管理,还可以使用time包中的Ticker和Timer来实现定时任务调度。
package main import ( \"fmt\" \"time\" ) func main() { ticker := time.NewTicker(1 * time.Second) defer ticker.Stop() for { select { case <-ticker.C: // 每隔1秒钟执行一次任务 fmt.Println("tick\") case <-time.After(5 * time.Second): // 当超过5秒钟后仍未收到ticker信号时,执行某个任务 fmt.Println(\"timeout\") return } } }
2、如何使用Go语言实现协程池?可以使用sync.WaitGroup来实现协程池,首先创建一个WaitGroup实例,然后在每次启动一个协程时将其加入到WaitGroup中,当协程执行完毕后,将其从WaitGroup中移除,最后在所有协程都执行完毕后调用WaitGroup的Wait方法等待所有协程结束。
package main import ( \"fmt\" \"sync\" ) func worker(id int, done *bool) { defer func() { // 将已完成的任务从WaitGroup中移除 done <true }() for { // 从队列中获取任务并执行 task := getTaskFromQueue() if task == nil { // 没有任务可执行时退出循环 break } // 执行任务 doTask(task) // 所有任务都已完成,通知WaitGroup继续等待下一个任务 <-done } } func main() { var waitGroup sync.WaitGroup // WaitGroup实例 gos_num := runtime.NumCPU() // CPU核心数 nos_limit := gos_num + len(queue)*2 // 同时最多启动的协程数(包括主协程) nos_limit = max(min(maxInt32, os_limit), minInt32) semaphore := make(chan bool, os_limit) done := make([]*bool, gos_num) semaphore <true // 将主协程加入到WaitGroup中 for i := range queue { semaphore <true // 将工作协程加入到WaitGroup中 go worker(i, &done[i]) // 在工作协程中执行worker函数 } semaphore <true // 将最后一个工作协程加入到WaitGroup中 waitGroup.Add(len(queue)) waitGroup.Wait() // 所有工作协程都完成时等待它们结束 semaphore <true // 将主协程从WaitGroup中移除并退出循环 memaphore <true // 将最后一个工作协程从WaitGroup中移除并退出循环
原创文章,作者:K-seo,如若转载,请注明出处:https://www.kdun.cn/ask/316866.html