一夜之间,又有两家独角兽取消IPO
06-18
goroutine是一个轻量级的线程。其优点是占用资源少、切换成本低、能够高效实现并发操作。
但是如何控制这些并发的goroutine呢? 说到并发控制,首先想到的就是锁。 Go还提供了锁相关的机制,包括互斥锁sync.Mutex和读写锁sync.RWMutex;另外,Go还提供了原子操作sync/atomic。
但这些操作是为了并发进程中的数据安全,而不是为了goroutine本身。 本文主要介绍goroutine并发行为的控制。
Go 中最常用的方法有 3 个:sync.WaitGroup、channel 和 Context。 1.sync.WaitGroupsync.WaitGroup是Go语言中非常有用的同步原语。
它可以帮助我们等待一组 goroutine 完成。我们通常在以下场景中使用sync.WaitGroup:当我们需要在main函数中等待一组goroutine完成后再退出程序时。
当我们需要在一个函数中启动多个goroutine并确保它们全部完成后再返回结果时。当我们需要在一个函数中启动多个 goroutine 并确保它们全部完成后再执行操作时。
当我们需要在一个函数中启动多个 goroutine 并确保它们在关闭资源之前全部完成时。当我们需要在一个函数中启动多个 goroutine 并确保它们在退出循环之前全部完成时。
使用sync.WaitGroup时,我们需要首先创建一个sync.WaitGroup对象,然后使用其Add方法指定要等待的goroutines的数量。然后,我们可以使用 go 关键字来启动多个 Goroutine,并使用每个 Goroutine 中的sync.WaitGroup 对象的 Done 方法来指示该 Goroutine 已经完成。
最后,我们可以使用sync.WaitGroup对象的Wait方法来等待所有goroutines完成。下面是一个简单的例子,会启动三个goroutine,分别休眠0s、1s、2s。
这三个goroutine完成后main函数就会退出: 代码语言: go copy package mainimport ("fmt" "sync" "time" )func main() {var wgsync.WaitGroupfor i := 0;我<3; i++ {wg.Add(1)go func(i int) {defer wg.Done()fmt.Printf("子协程睡眠: %ds\n", i)time.Sleep(time.Duration(i) * time .Second)}(i)}wg.Wait()fmt.Println("主函数完成")}2. Go语言中的channel,使用channel可以帮助我们更好的控制goroutine的并发度。下面是一些常用的使用通道控制 Goroutine 并发的方法: 2.1 使用无缓冲通道进行同步 我们可以使用无缓冲通道来实现生产者-消费者模式,其中一个 Goroutine 负责生产数据,另一个 Goroutine 负责生产数据。
消耗数据。当生产者 Goroutine 向通道发送数据时,消费者 Goroutine 会阻塞等待数据到达。
这样我们就保证了生产者和消费者之间的数据同步。下面是一个简单的示例代码: 代码语言: go copy package mainimport ( "fmt" "sync" "time")func Producer(ch chan int, wg *sync.WaitGroup) { defer wg.Done() for i := 0;我<10; i++ { ch <- i fmt.Println("产生", i) time.Sleep(* time.Millisecond) } close(ch)}func Consumer(ch chan int, wg *sync.WaitGroup) { defer wg.Done( ) for i := range ch { fmt.Println("consumed", i) time.Sleep(* time.Millisecond) }}func main() { var wgsync.WaitGroup ch := make( chan int) wg.Add (2) go Producer(ch, &wg) go Consumer(ch, &wg) wg.Wait()} 在这个例子中,我们创建一个无缓冲的通道,用于生产者 Goroutine 和消费者 Goroutine 之间传递数据。
生产者 Goroutine 向通道发送数据,消费者 Goroutine 从通道接收数据。在生产者goroutine中,我们使用time.Sleep函数来模拟生产数据的时间。
在消费者goroutine中,我们使用time.Sleep函数来模拟消费数据的时间。最后,我们使用sync.WaitGroup来等待所有goroutine完成。
2.2 使用缓冲通道进行限流 我们可以使用缓冲通道来限制并发 goroutine 的数量。在这种情况下,我们可以将通道的容量设置为我们想要的并发 goroutine 的最大数量。
然后,在启动每个 goroutine 之前,我们向通道发送一个值。 Goroutine 完成后,我们从通道接收一个值。
这样我们就可以保证同时运行的goroutine数量不超过我们指定的最大并发数。下面是一个简单的示例代码: 代码语言: go copy package mainimport ("fmt" "sync")func main() { var wgsync.WaitGroup maxConcurrency := 3 semaphore := make(chan struct{}, maxConcurrency) for我:= 0;我<10; i++ { wg.Add(1) go func() { defer wg.Done() 信号量 <- struct{}{} fmt.Println("goroutine", i, "started") // 做一些工作 fmt.Println( "goroutine", i, "finished") <-semaphore }() } wg.Wait()} 在这个例子中,我们创建了一个缓冲区大小为 3 的缓冲通道。
然后,我们启动了 10 个 goroutine。在每个 goroutine 中,我们向通道发送一个空结构,表示该 goroutine 已经开始执行。
Goroutine 完成后,我们从通道收到一个空结构,表明 Goroutine 已完成执行。这样我们就可以保证同时运行的goroutine数量不超过3个。
3. Context 在Go语言中,使用Context可以帮助我们更好的控制goroutine的并发度。下面是一些常用的使用 Context 来控制 goroutine 并发的方法: 3.1 超时控制 在某些情况下,我们需要限制 goroutine 的执行时间,以避免程序长期阻塞或者死锁等问题。
使用Context可以帮助我们更好的控制goroutine的执行时间。我们可以创建一个带有超时的 Context 并将其传递给 goroutine。
如果goroutine在超时时间内没有完成执行,我们可以使用Context的Done方法来取消goroutine的执行。下面是一个简单的示例代码: 代码语言: go copy package mainimport ( "context" "fmt" "time")func main() { ctx, cancel := context.WithTimeout(context.Background(), 2*time.其次) defer cancel() go func() { for { select { case <-ctx.Done(): fmt.Println("goroutine finish") return default: fmt.Println("goroutine running") time.Sleep(* time.Millisecond) } } }() time.Sleep(3 * time.Second)} 在此示例中,我们创建一个带有超时的 Context,然后将其传递给 goroutine。
在goroutine中,我们使用select语句来监听Context的Done方法。如果 Context 超时,我们将取消 goroutine 的执行。
3.2 取消操作 在某些情况下,我们需要在程序运行时取消某些goroutine的执行。使用Context可以帮助我们更好的控制goroutine的取消操作。
我们可以创建一个具有取消功能的 Context 并将其传递给 goroutine。如果我们需要取消goroutine的执行,可以使用Context的Cancel方法来取消goroutine的执行。
下面是一个简单的示例代码: 代码语言: go copy package mainimport ( "context" "fmt" "sync" "time")func main() { ctx, cancel := context.WithCancel(context.Background()) var wgs??ync.WaitGroup wg.Add(1) go func() { defer wg.Done() for { select { case <-ctx.Done(): fmt.Println("goroutine finish") return default: fmt.Println( "goroutine running") time.Sleep(* time.Millisecond) } } }() time.Sleep(2 * time.Second) cancel() wg.Wait()} 在这个例子中,我们创建了一个函数的上下文,然后传递给 goroutine。在goroutine中,我们使用select语句来监听Context的Done方法。
如果 Context 被取消,我们就会取消 goroutine 的执行。在main函数中,我们使用time.Sleep函数来模拟程序运行过程中某个时刻需要取消goroutine的执行,然后调用Context的Cancel方法来取消goroutine的执行。
3.3 资源管理 在某些情况下,我们需要对 goroutine 使用的资源进行管理,以避免资源泄漏或竞争条件等问题。使用Context可以帮助我们更好的管理goroutine使用的资源。
我们可以将资源与Context关联起来,然后将Context传递给goroutine。当goroutine执行完成后,我们可以使用Context来释放资源或者进行其他资源管理操作。
下面是一个简单的示例代码: 代码语言: go copy package mainimport ( "context" "fmt" "sync" "time")func worker(ctx context.Context, wg *sync.WaitGroup) { defer wg.Done() for { select { case <-ctx.Done(): fmt.Println("goroutine finish") return default: fmt.Println("goroutine running") time.Sleep(* time.Millisecond) } }}func main() { ctx, cancel := context.WithCancel(context.Background()) var wgsync.WaitGroup wg.Add(1) go worker(ctx, &wg) time.Sleep(2 * time.Second) cancel() wg.Wait ()} 在此示例中,我们创建一个具有取消功能的 Context,然后将其传递给 goroutine。在goroutine中,我们使用select语句来监听Context的Done方法。
如果 Context 被取消,我们就会取消 goroutine 的执行。在main函数中,我们使用time.Sleep函数来模拟程序运行过程中某个时刻需要取消goroutine的执行,然后调用Context的Cancel方法来取消goroutine的执行。
我正在参加第五期腾讯科技创造特训营有奖征文比赛。快来和我分享奖品吧!免责声明:本作品根据 Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) 许可证获得许可。
使用时请注明来源。
版权声明:本文内容由互联网用户自发贡献,本站不拥有所有权,不承担相关法律责任。如果发现本站有涉嫌抄袭的内容,欢迎发送邮件 举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。
标签:
相关文章
06-18
06-18
06-17
06-18
06-17
06-18
最新文章
【玩转GPU】ControlNet初学者生存指南
【实战】获取小程序中用户的城市信息(附源码)
包雪雪简单介绍Vue.js:开学
Go进阶:使用Gin框架简单实现服务端渲染
线程池介绍及实际案例分享
JMeter 注释 18 - JMeter 常用配置组件介绍
基于Sentry的大数据权限解决方案
【云+社区年度征文集】GPE监控介绍及使用