Golang异步编程方式和技巧

腾讯技术工程 发表于 8月以前  | 总阅读数:500 次

Golang基于多线程、协程实现,与生俱来适合异步编程,当我们遇到那种需要批量处理且耗时的操作时,传统的线性执行就显得吃力,这时就会想到异步并行处理。下面介绍一些异步编程方式和技巧。

作者:zvalhu

一、使用方式

1.1、最简单的最常用的方式:使用go关键词

func main() {
 go func() {
  fmt.Println("hello world1")
 }()
 go func() {
  fmt.Println("hello world2")
 }()
}

或者:

func main() {
 go Announce("hello world1")
 go Announce("hello world2")
}
func Announce(message string) {
 fmt.Println(message)
}

使用匿名函数传递参数

data := "Hello, World!"
go func(msg string) {
      // 使用msg进行异步任务逻辑处理
      fmt.Println(msg)
}(data)

这种方式不需要考虑返回值问题,如果要考虑返回值,可以使用下面的方式。

1.2、通过goroutine和channel来实现

ch := make(chan int, 1) // 创建一个带缓冲的channel
// ch := make(chan int, 0) // 创建一个无缓冲的channel

go func() {
    // 异步任务逻辑
    ch <- result // 将结果发送到channel
    // 异步任务逻辑
    close(ch) // 关闭channel,表示任务完成
}()
// 在需要的时候从channel接收结果
result := <-ch

1.3、使用sync.WaitGroup

sync.WaitGroup用于等待一组协程完成其任务。通过Add()方法增加等待的协程数量,Done()方法标记协程完成,Wait()方法阻塞直到所有协程完成。

var wg sync.WaitGroup

// 启动多个协程
for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(index int) {
        defer wg.Done()
        // 异步任务逻辑
    }(i)
}

// 等待所有协程完成
wg.Wait()

1.4、使用errgroup实现协程组的错误处理

如果想简单获取协程返回的错误,errgroup包很适合,errgroup包是Go语言标准库中的一个实用工具,用于管理一组协程并处理它们的错误。可以使用errgroup.Group结构来跟踪和处理协程组的错误。

var eg errgroup.Group
for i := 0; i < 5; i++ {
    eg.Go(func() error {
     return errors.New("error")
    })

    eg.Go(func() error {
     return nil
    })
}

if err := eg.Wait(); err != nil {
    // 处理错误
}

二、一些使用技巧

2.1、使用channel的range和close操作

range操作可以在接收通道上迭代值,直到通道关闭。可以使用close函数关闭通道,以向接收方指示没有更多的值。

ch := make(chan int)

go func() {
    for i := 0; i < 5; i++ {
        ch <- i // 发送值到通道
    }
    close(ch) // 关闭通道
}()

// 使用range迭代接收通道的值
for val := range ch {
    // 处理接收到的值
}

2.2、使用select语句实现多个异步操作的等待

ch1 := make(chan int)
ch2 := make(chan string)

go func() {
    // 异步任务1逻辑
    ch1 <- result1
}()

go func() {
    // 异步任务2逻辑
    ch2 <- result2
}()

// 在主goroutine中等待多个异步任务完成
select {
case res1 := <-ch1:
    // 处理结果1
case res2 := <-ch2:
    // 处理结果2
}

2.3、使用select和time.After()实现超时控制

如果需要在异步操作中设置超时,可以使用select语句结合time.After()函数实现。

ch := make(chan int)

go func() {
    // 异步任务逻辑
    time.Sleep(2 * time.Second)
    ch <- result
}()

// 设置超时时间
select {
case res := <-ch:
    // 处理结果
case <-time.After(3 * time.Second):
    // 超时处理
}

2.4、使用select和time.After()实现超时控制

如果需要在异步操作中设置超时,可以使用select语句结合time.After()函数实现。

ch := make(chan int)

go func() {
    // 异步任务逻辑
    time.Sleep(2 * time.Second)
    ch <- result
}()

// 设置超时时间
select {
case res := <-ch:
    // 处理结果
case <-time.After(3 * time.Second):
    // 超时处理
}

2.5、使用time.Tick()和time.After()进行定时操作

time.Tick()函数返回一个通道,定期发送时间值,可以用于执行定时操作。time.After()函数返回一个通道,在指定的时间后发送一个时间值。

tick := time.Tick(1 * time.Second) // 每秒执行一次操作

for {
    select {
    case <-tick:
        // 执行定时操作
    }
}

select {
case <-time.After(5 * time.Second):
    // 在5秒后执行操作
}

2.6、使用sync.Mutex或sync.RWMutex进行并发安全访问

当多个协程并发访问共享数据时,需要确保数据访问的安全性。sync.Mutex和sync.RWMutex提供了互斥锁和读写锁,用于在访问共享资源之前进行锁定,以避免数据竞争。sync.RWMutex是一种读写锁,可以在多个协程之间提供对共享资源的并发访问控制。多个协程可以同时获取读锁,但只有一个协程可以获取写锁。

var mutex sync.Mutex
var data int

// 写操作,使用互斥锁保护数据
mutex.Lock()
data = 123
mutex.Unlock()

// 读操作,使用读锁保护数据
//RLock()加读锁时,如果存在写锁,则无法加读锁;当只有读锁或者没有锁时,可以加读锁,读锁可以加载多个
mutex.RLock()
value := data
mutex.RUnlock()

var rwMutex sync.RWMutex
var sharedData map[string]string

// 读操作,使用rwMutex.RLock读锁保护数据
func readData(key string) string {
    rwMutex.RLock()
    defer rwMutex.RUnlock()
    return sharedData[key]
}

// 写操作,使用rwMutex.Lock写锁保护数据
func writeData(key, value string) {
    rwMutex.Lock()
    defer rwMutex.Unlock()
    sharedData[key] = value
}

注意:sync.Mutex 的锁是不可以嵌套使用的 sync.RWMutex 的 RLock()是可以嵌套使用的 sync.RWMutex 的 mu.Lock() 是不可以嵌套的 sync.RWMutex 的 mu.Lock() 中不可以嵌套 mu.RLock()

2.7、使用sync.Cond进行条件变量控制

sync.Cond是一个条件变量,用于在协程之间进行通信和同步。它可以在指定的条件满足之前阻塞等待,并在条件满足时唤醒等待的协程。

var cond = sync.NewCond(&sync.Mutex{})
var ready bool

go func() {
    // 异步任务逻辑
    ready = true

    // 通知等待的协程条件已满足
    cond.Broadcast()
}()

// 在某个地方等待条件满足
cond.L.Lock()
for !ready {
    cond.Wait()
}
cond.L.Unlock()

2.8、使用sync.Pool管理对象池

sync.Pool是一个对象池,用于缓存和复用临时对象,可以提高对象的分配和回收效率。

type MyObject struct {
    // 对象结构
}

var objectPool = sync.Pool{
    New: func() interface{} {
        // 创建新对象
        return &MyObject{}
    },
}

// 从对象池获取对象
obj := objectPool.Get().(*MyObject)

// 使用对象

// 将对象放回对象池
objectPool.Put(obj)

2.9、使用sync.Once实现只执行一次的操作

sync.Once用于确保某个操作只执行一次,无论有多少个协程尝试执行它,常用于初始化或加载资源等场景。

var once sync.Once
var resource *Resource

func getResource() *Resource {
    once.Do(func() {
        // 执行初始化资源的操作,仅执行一次
        resource = initResource()
    })
    return resource
}

// 在多个协程中获取资源
go func() {
    res := getResource()
    // 使用资源
}()

go func() {
    res := getResource()
    // 使用资源
}()

2.10、使用sync.Once和context.Context实现资源清理

可以结合使用sync.Once和context.Context来确保在多个协程之间只执行一次资源清理操作,并在取消或超时时进行清理。

var once sync.Once

func cleanup() {
    // 执行资源清理操作
}

func doTask(ctx context.Context) {
    go func() {
        select {
        case <-ctx.Done():
            once.Do(cleanup) // 只执行一次资源清理
        }
    }()

    // 异步任务逻辑
}

2.11、使用sync.Map实现并发安全的映射

sync.Map是Go语言标准库中提供的并发安全的映射类型,可在多个协程之间安全地进行读写操作。

var m sync.Map

// 存储键值对
m.Store("key", "value")

// 获取值
if val, ok := m.Load("key"); ok {
    // 使用值
}

// 删除键
m.Delete("key")

2.12、使用context.Context进行协程管理和取消

context.Context用于在协程之间传递上下文信息,并可用于取消或超时控制。可以使用context.WithCancel()创建一个可取消的上下文,并使用context.WithTimeout()创建一个带有超时的上下文。

ctx, cancel := context.WithCancel(context.Background())

go func() {
    // 异步任务逻辑
    if someCondition {
        cancel() // 取消任务
    }
}()

// 等待任务完成或取消
select {
case <-ctx.Done():
    // 任务被取消或超时
}

2.13、使用context.WithDeadline()和context.WithTimeout()设置截止时间

context.WithDeadline()和context.WithTimeout()函数可以用于创建带有截止时间的上下文,以限制异步任务的执行时间。

func doTask(ctx context.Context) {
    // 异步任务逻辑

    select {
    case <-time.After(5 * time.Second):
        // 超时处理
    case <-ctx.Done():
        // 上下文取消处理
    }
}

func main() {
    ctx := context.Background()
    ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
    defer cancel()

    go doTask(ctx)

    // 继续其他操作
}

2.14、使用context.WithValue()传递上下文值

context.WithValue()函数可用于在上下文中传递键值对,以在协程之间共享和传递上下文相关的值。

type keyContextValue string

func doTask(ctx context.Context) {
    if val := ctx.Value(keyContextValue("key")); val != nil {
        // 使用上下文值
    }
}

func main() {
    ctx := context.WithValue(context.Background(), keyContextValue("key"), "value")
    go doTask(ctx)

    // 继续其他操作
}

2.15、使用atomic包进行原子操作

atomic包提供了一组函数,用于实现原子操作,以确保在并发环境中对共享变量的读写操作是原子的。

var counter int64

func increment() {
    atomic.AddInt64(&counter, 1)
}

func main() {
    var wg sync.WaitGroup

    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            increment()
        }()
    }

    wg.Wait()
    fmt.Println("Counter:", counter)
}

本文由微信公众号腾讯技术工程原创,哈喽比特收录。
文章来源:https://mp.weixin.qq.com/s/PuNu65ggHyB6jxRqhbN_VQ

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 目录