Golang 并发 WaitGroup

Golang 并发 WaitGroup

WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。

WaitGroup 是一个计数信号量,可以用来记录并维护运行的 goroutine。如果 WaitGroup
的值大于 0,Wait 方法就会阻塞。

WaitGroup 使用

  • func (wg *WaitGroup) Add(delta int)
  • func (wg *WaitGroup) Done()
  • func (wg *WaitGroup) Wait()

示例如下:

func TestWaitGroup(t *testing.T) {
	var wg sync.WaitGroup

	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("1 goroutine sleep ...")
		time.Sleep(2 * time.Second)
		fmt.Println("1 goroutine exit ...")
	}()

	wg.Add(1)
	go func() {
		defer wg.Done()
		fmt.Println("2 goroutine sleep ...")
		time.Sleep(4 * time.Second)
		fmt.Println("2 goroutine exit ...")
	}()

	fmt.Println("waiting for all goroutine ")
	wg.Wait()
	fmt.Println("All goroutines finished!")
}

输出结果

waiting for all goroutine
2 goroutine sleep ...
1 goroutine sleep ...
1 goroutine exit ...
2 goroutine exit ...
All goroutines finished!

异常:

要注意Add和Done函数一定要配对,否则可能发生死锁,所报的错误信息如下:

fatal error: all goroutines are asleep - deadlock!

WaitGroup在需要等待多个任务结束再返回的业务来说还是很有用的。

但现实中用的更多的可能是,先等待一个协程组,若所有协程组都正确完成,则一直等到所有协程组结束;若其中有一个协程发生错误,则告诉协程组的其他协程,全部停止运行(本次任务失败)以免浪费系统资源。

该场景WaitGroup是无法实现的,那么该场景该如何实现呢,就需要用到通知机制,其实也可以用channel来实现。

WaitGroup 时如何实现超时机制

WaitGroup如何实现超时机制呢?任务不能一直等待下去,如果超时之后,希望程序可以继续进行,不影响主流程的调用。

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    wg := sync.WaitGroup{}
    wg.Add(1)

    done := make(chan struct{})

    go func() {
        wg.Wait()
        done <- struct{}{}
    }()

    go func() {
        time.Sleep(5 * time.Second)
        wg.Done()
    }()

    timeout := time.Duration(10) * time.Second
    fmt.Printf("Wait for waitgroup (up to %s)\n", timeout)

    select {
    case <-done:
        fmt.Printf("Wait group finished\n")
    case <-time.After(timeout):
        fmt.Printf("Timed out waiting for wait group\n")
    }

    fmt.Printf("Free at last\n")
}

WaitGroup 一组协程之间,其中一个协程发生错误,如何全部停止

等待一个协程组全部正确完成则结束;但其中一个协程发生错误,这时候就会阻塞,全部停止运行(本次任务失败)以免浪费系统资源,此时可以结合通道(channel)或者 select 语句两种方式来处理。

1、考虑使用两个通道:一个用于报告错误,另一个用于通知所有协程停止。在协程内部捕获错误,并将错误信息发送到错误通道。另一个协程监听错误通道,一旦有错误发生,就会向停止通知通道发送信号,通知所有协程停止运行。



func worker(id int, errCh chan error, wg *sync.WaitGroup) {
	defer wg.Done()

	// 模拟错误
	if id == 2 {
		errCh <- fmt.Errorf("error occurred in worker %d", id)
		return
	}

	// 做一些工作
	fmt.Printf("Worker %d is working...\n", id)
}

func TestWaitGroupError(t *testing.T) {
	var wg sync.WaitGroup
	errCh := make(chan error)
	stopCh := make(chan struct{})

	for i := 0; i < 5; i++ {
		wg.Add(1)
		go worker(i, errCh, &wg)
	}

	go func() {
		err := <-errCh
		if err != nil {
			fmt.Println("Error occurred:", err)
			close(stopCh)
		}
	}()

	go func() {
		wg.Wait()
		close(errCh)
	}()

	// 等待通知,停止所有协程
	<-stopCh
	fmt.Println("All workers stopped.")
}

errCh 通道用于发送错误信息,stopCh 通道用于通知所有协程停止。如果有一个协程发生错误,就会关闭 stopCh 通道,通知其他协程停止运行。

2、select 语句是 Go 中用于多路非阻塞的关键字,可以用于监听多个通道的操作。可以在 select 语句中组合使用 WaitGroup 和通道来实现对协程组的控制。

参考

https://www.cnblogs.com/beatle-go/p/17881910.html

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值