首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

使用sync.WaitGroup panics完成的WebCrawl练习。我做错了什么?Go惯用解决方案是什么?

在使用sync.WaitGroup进行并发编程时,panic通常是由于以下几个原因造成的:

  1. 未正确初始化WaitGroup:确保在使用WaitGroup之前已经调用了Add方法来设置计数器。
  2. 忘记调用Done方法:每个启动的goroutine在完成工作后都应该调用Done方法来减少计数器。
  3. 在WaitGroup计数器归零之前访问共享资源:这可能导致竞态条件,从而引发panic
  4. 在WaitGroup上执行阻塞操作:例如,在Wait方法调用之后,不应该再有其他可能阻塞的操作,否则可能会导致死锁。

Go惯用解决方案

以下是一个使用sync.WaitGroup进行WebCrawl的示例代码,展示了如何正确使用WaitGroup以避免panic

代码语言:txt
复制
package main

import (
    "fmt"
    "net/http"
    "sync"
)

func crawl(url string, wg *sync.WaitGroup) {
    defer wg.Done() // 确保在函数返回前调用Done

    resp, err := http.Get(url)
    if err != nil {
        fmt.Println(err)
        return
    }
    defer resp.Body.Close()

    // 处理网页内容...
    fmt.Println("Crawled", url)
}

func main() {
    var wg sync.WaitGroup
    urls := []string{
        "http://example.com",
        "http://example.org",
        "http://example.net",
    }

    for _, url := range urls {
        wg.Add(1) // 在启动goroutine之前调用Add
        go crawl(url, &wg)
    }

    wg.Wait() // 等待所有goroutine完成
    fmt.Println("Crawling finished")
}

关键点解释

  1. 初始化WaitGroup:在main函数中初始化sync.WaitGroup
  2. 调用Add方法:在启动每个goroutine之前,调用wg.Add(1)来增加计数器。
  3. 调用Done方法:在每个goroutine的函数末尾,使用defer wg.Done()来确保计数器在函数返回前减少。
  4. 等待所有goroutine完成:在main函数中调用wg.Wait()来等待所有goroutine完成。

参考链接

通过遵循这些最佳实践,可以有效地避免在使用sync.WaitGroup时出现panic的情况。

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

看了这款Go并发库后, 感觉以后不用造轮子了,还支持范型

type WaitGroup struct { wg sync.WaitGroup pc panics.Catcher } // Go spawns a new goroutine in the...简单可以在闭包 doSomething 运行时增加一个 safeGo 函数,用于捕捉 recover 原生 Go 要生成大量无用代码,司 repo 运动式清理过一波,也遇到过 goroutine...相比我司现有 concurrency 库 增加了泛型实现 增加了对 goroutine 复用 增加并发度设置(司有,但 conc 实现方式更巧秒) 支持函数签名更多 先看一下支持接口 Go(f...如果 Wait 提前结束了会发生什么?...[]T 拿到部分结果只能丢弃,返回给上层 timeout error Context 框架传递参数 通用库很容易臃肿,司并发库会给闭包产生新 context, 并继承所需框架层 metadata

26320
  • Golang并发:再也不愁选channel还是选锁

    如果自己心里还没有清晰答案,那就读下这篇文章,你会了解到: 使用channel解决并发问题核心思路和示例 channel擅长解决什么并发问题,Mutex擅长解决什么并发问题 一个并发问题该怎么入手解解决...这个思路是从Go语言核心开发者演讲中学来,然而视频已经找不到了,不然直接共享给大家,他提到了Golang并发核心实践4个点: DataFlow -> Drawing -> Pipieline...顺便看下同一个并发问题,使用channel和Mutex解决是什么差别。...但这个例子展示了3点: 使用channel解决并发问题核心在于关注数据流动 channel不一定是某个并发问题最好解决方案 map在并发中,可以不用锁进行保护,而是使用channel 现在,回到了开篇问题.../golang/go/wiki/MutexOrChannel 上一篇文章《Golang并发次优选择:sync包》,WaitGroup用在worker pool上示例代码贴错了,非常抱歉,以后写完文章后

    2.5K41

    Go 单例模式

    最近有很多关于使用Ruby语言公司切换到Go、体验Go语言、和Go并行和并发解决问题文章。   ...激进锁   也看到一些使用糟糕方法来解决线程安全问题。...在高并发代码基础上,这会产生瓶颈,因为在同一时间只有一个go routine可以得到单例实例。      所以这不是最好方法,我们找找其他解决方案。  ...Go语言和标准库源码看一下go routines 同步实现方式来更好  Go惯用单例方法    我们想要使用Go惯用手法来实现这个单例模式。...所有的切换到Go语言新开发者,需要明确理解线程安全原理才能更好改善你代码。即使Go语言本身通过做了很多努力允许你使用很少并发知识来设计并发代码。

    1K100

    Go嵌套并发实现EDM,附坑点分析#1

    看着身边优秀小伙伴们早就开始写博客,自己深感落后,还好迟总比不做好,勉励自己见贤思齐。趁着年前最后一个周末,阳光正好,写下第一篇博客,为2019年开个头,以期完成今年为自己立下flags。...从PHPer转Gopher,很大一个原因就是业务对性能和并发持续需求,另一个主要原因就是Go语言原生并发特性,可以在提供同等高可用能力下,使用更少机器资源,节约可观成本。...而分批发送即使中途出错了,下一次重新run可从上次出错end行开始,最多是[start - end]一个batchLength 发送失败,可以接受。...主协程等待全部子协程退出后,再优雅退出主协程;嵌套使用时注意wg.Wait()放位置; b) 合理使用channel,无缓冲chan将阻塞当前goroutine,有缓冲chan在cap未满情况下不会阻塞当前...goroutine,使用完记得释放chan资源; c) 注意函数间传值或传引用(本质上还是传值,传指针指针内存值)合理使用; 后记:第一篇博客写到这里差不多算完成了,一不小心一个下午就过去了,写逻辑

    59520

    并发编程包之 errgroup

    前言 哈喽,大家好,是asong,今天给大家介绍一个并发编程包errgroup,其实这个包就是对sync.waitGroup封装。...我们在之前文章—— 源码剖析sync.WaitGroup(文末思考题你能解释一下吗?)...,从源码层面分析了sync.WaitGroup实现,使用waitGroup可以实现一个goroutine等待一组goroutine干活结束,更好实现了任务同步,但是waitGroup却无法返回错误,...Done()方法控制是否结束 如果有一个函数f运行出错了,我们把它保存起来,如果有cancel()方法,则执行cancel()取消其他goroutine 这里大家应该会好奇为什么使用errOnce,也就是...errGroup中没有panic处理,我们在Go方法中传入func() error方法时要保证程序健壮性 踩坑日记 使用errGroup也并不是一番风顺之前在项目中使用errGroup就出现了一个

    47220

    Golang之Context迷思

    而言,Golang 中 Context 一直是谜一样存在,如果你还不了解它,建议阅读「快速掌握 Golang context 包,简单示例」,本文主要讨论一些曾经疑问。...Context 到底是干什么?...,保证你想死心都有了。...实际上这是因为 Context 实现了继承,可以完成更复杂操作,虽然我们自己编码也能实现,但是通过使用 Context,可以让代码更标准化一些,下面引用「如何正确使用 Context – Jack Lindamood...实际使用中,任何有可能「慢」方法都应该考虑通过 Context 实现退出机制,以避免因为无法退出导致泄露问题,对于服务端编程而言,通常意味着你很多方法第一个参数都会是 Context,虽然丑爆了,但在出现更好解决方案之前

    38720

    手摸手Go 接口与反射

    反射是把双刃剑,虽然代码更加灵活了但是 代码阅读起来也困难了 一定程度上破坏了静态类型语言编译期检查 运行时会有panic风险 降低了系统性能 我们为什么需要反射?...类型一个重要类别是接口类型,接口可以存储任何非接口具体值,只要该值实现了接口方法即可。 接口 接口是多个方法声明集合,侧重于做什么,不关系怎么 谁来。...Go接口机制比较简洁,不像Java需要显示声明实现接口,Go只要目标类型方法集中包含了接口声明全部方法,就被称为实现了该接口,无须显示声明。...interface{} 包含一组方法接口 Go语言使用runtime.eface表示不包含任何方法接口,runtime.iface表示包含一组方法接口。...但是通过reflect包提供类似动态语言功能,你可以运行时获取参数Value和Type进而完成一些特定需求。其转换关系如图 reflect4

    39420

    Go语言实战笔记(十二)| Go goroutine

    那么线程是什么呢?线程是一个执行空间,比如要下载一个文件,访问一次网络等等。线程会被操作系统调用,来在不同处理器上运行编写代码任务,这个处理器不一定是该程序进程所在处理。...所以并发概念和并行不一样,并行指的是在不同物理处理器上同时执行不同代码片段,并行可以同时很多事情,而并发是同时管理很多事情,因为操作系统和硬件总资源比较少,所以并发效果要比并行好的多,使用较少资源更多事情...这里sync.WaitGroup其实是一个计数信号量,使用目的是要main函数等待两个goroutine执行完成后再结束,不然这两个goroutine还在运行时候,程序就结束了,看不到想要结果...sync.WaitGroup使用也非常简单,先是使用Add 方法设设置计算器为2,每一个goroutine函数执行完之后,就调用Done方法减1。...默认情况下,Go默认是给每个可用物理处理器都分配一个逻辑处理器,因为电脑是4核,所以上面的例子默认创建了4个逻辑处理器,所以这个例子中同时也有并行调度,如果我们强制只使用一个逻辑处理器,我们再看看结果

    37530

    一篇文章带你了解Go语言基础之并发(channel)

    前言 Hi,大家好,是码农,星期八,本篇继续带来Go语言并发基础,channel如何使用。 看看Go协程如何配合channel。 快来上车叭。...为什么需要channel channel在Go中,也叫做管道,是用来多线程之间共享数据。 通常情况下,在Go中共享数据用也是channel,但是在Go有两种共享数据方式。 共享内存实现通讯。...报错是因为在main中完成了发送值和取值两个操作,所以会出现上述问题,但是结果是没有错。...快递员(快递柜) go 张三(快递柜) go 李四(快递柜) wg.Wait() } 总结 上述讲述了Go语言并发如何和channel配合使用,毕竟我们一般任务都不是单独运行...代码中使用了中文命名变量名是为了好看,实际开发中千万不要这样!!! 上述代码一定要敲一下,如果在操作过程中有任何问题,记得下面留言,我们看到会第一时间解决问题。

    47320

    《刻意练习》(上):人人都能成为天才!

    本书结构 按照惯用方法,先把这本书9章内容进行了划分: 前四章主要是讲基本概念:怎么练习才有效,大脑怎么发生变化,什么是心理表征,还有怎么才是刻意练习。...所以这样没做好练习方法一定是出问题了,那么解决这个问题钥匙是什么呢?书中答案是要有目的练习。 和“有目的”相对是“天真的”练习,就是只是反复某个事情,并指望这种反复就能够提高水平。...今天要记住20个单词 需要反馈来告诉你还有什么存在不足。20个单词里面有5个错了 如果不走出舒适区,你就无法进步。貌似20个没有压力了,那就30个吧 在练习过程中遭遇瓶颈怎么办呢?...要主动调整练习,需要自己思考如何能够更好,而不是简单什么什么 要包含反馈,不对要尽快纠正 可以产生心理表征,然后这些心理表征又能够帮助我们纠正练习 技能难度是逐步提高。...这种方式帮助我看到每个考题都是在脑海中浮现那个脑图出来然后定位到这个题是那一部分哪个考点下面的,应该使用什么方法来求解。

    70310

    深度阅读之《100 Go Mistakes and How to Avoid Them》

    前不久曹大连接发了几个关于《100 mistakes》视频,多猜他大都是看看标题,看看代码,就知道要说什么了,并且很快就跳过去,速度飞快。开始设想是除了读懂内容,还想练习一下英语阅读,慢就慢吧。...不过,过后也确实加快了速度,毕竟人家半小时进度要两周,稍微有点离谱。 简单谈一下这本书:全书“凑”了 100 个关于 Go 错误。...函数名反映它做了什么,而不是怎么。虽然命名一直是编程界难题,但不断尝试好命名也是必要。日常 util, common, base 这些包名其实并不好。...函数代码比较长时,还是带上比较好,增加可读性,不然看代码的人一直要记住返回值是什么。 在同一个函数里,统一返回值风格,不要一会儿返回带名字参数,一会儿又直接 return。...问chatGPT关于data race有什么坏处,得到回答: sync.WaitGroup 正确用法是:在父 goroutine 中调用 Add 方法,在子 goroutine 中调用 Done

    1K10

    Java开发者Golang进修指南:从0->1带你实现协程池

    在Java编程中,为了降低开销和优化程序效率,我们常常使用线程池来管理线程创建和销毁,并尽量复用已创建对象。这样不仅可以提高程序运行效率,还能减少垃圾回收器对对象回收次数。...因此,在Golang中,我们仍然需要考虑使用协程池情况,并根据具体场景来选择最佳解决方案。今天,我们将从Java线程池角度出发,手把手地带你实现一个Golang协程池。...如果你觉得有些困难,记住我们宗旨是使用固定数量协程来处理所有的任务。这样好处是可以避免协程数量过多导致资源浪费,也能确保任务有序执行。让我们一起开始,一步步地构建这个Golang协程池吧!...这个channel类似于我们想要获取任务队列。与Java中使用链表形式并通过独占锁获取共享链表这种临界资源方式不同,选择使用了Golang中channel来进行通信。...因为Golang更倾向于使用channel进行通信,而不是共享资源。所以,选择了使用channel。为了避免在添加任务时直接阻塞,特意创建了一个带有任务缓冲channel。

    28320

    go-并发

    goroutine 是由Go语言运行时(runtine)调度完成,而线程是由操作系统调度完成Go语言还提供 channel 在多个 goroutine 间进行通信。 ...我们可以使用内置 len 函数获取通道内元素数量,使用 cap 函数获取通道容量,虽然我们很少会这么。...sync.WaitGroup 在代码中生硬使用 time.Sleep 肯定是不合适Go语言中可以使用 sync.WaitGroup 来实现并发任务同步。  ...Go语言中 sync 包中提供了一个针对只执行一次场景解决方案– sync.Once 。...针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供方法它在用户态就可以完成,因此性能比加锁操作更好。Go语言中原子操作由内置标准库 sync/atomic 提供。

    68820

    从一个WaitGroup例子看Go语言Upvalue传递

    Go语言闭包捕获外部变量,还是习惯以Lua叫法,称之为Upvalue,毕竟Go借鉴了很多Lua特性。 让我们首先看五个几乎一样代码片段。...main.main() /Users/linkerlin/gos/wgtest1.go:17 +0xba exit status 2 这是因为Go语言中WaitGroup是一个不可以在第一次使用后复制对象...因为upvaulei是byRef传递。注意,这里出现了4个5和一个4,最终输出什么其实是随机,取决于操作系统和硬件。goroutine调度越快,就越可能出现比5小输出。...那么我们推荐写法应该是什么样子呢?...综上所述,处于降低开发人员心智负担考虑,建议:     1. Go语言里面的goroutine入口函数不要传递参数。     2. 所有的传ref参数都通过upvalue来捕获。

    92840
    领券