In a concurrent scenario, will the mixed use of waitgroup and goroutine cause the program to be abnormal or hang up?

What did you do?

type waitGroup struct {
	sync.WaitGroup
}

func (w *waitGroup) Wrap(fn func()) {
	w.Add(1)
	go func() {
		fn()
		w.Done()
	}()
}

func (w *waitGroup) WrapRecover(fn func()) {
	w.Add(1)
	go func() {
		defer func() {
			if err := recover(); err != nil {
				log.Println("exec recover: ", err)
			}
		}()

		fn()
		w.Done()
	}()
}

func getData(i int64) string {
	return strconv.FormatInt(i, 10)
}

func demo(ctx context.Context) error {
	go func() {
		ticker := time.NewTicker(500 * time.Millisecond)
		defer ticker.Stop()
		var i int64
		var wg = &waitGroup{}
		for {
			select {
			case <-ticker.C:
				data := getData(i)
				i++
				wg.Wrap(func() {
					log.Println("data: ", data)
				})
			case <-ctx.Done():
				wg.Wait()
				return
			}
		}
	}()

	return nil
}

main.go

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"strconv"
	"sync"
	"syscall"
	"time"
)

var (
	port = 1339
	wait = 3 * time.Second
)

func init() {
	httpMux := http.NewServeMux()
	httpMux.HandleFunc("/debug/pprof/", pprof.Index)
	httpMux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	httpMux.HandleFunc("/debug/pprof/profile", pprof.Profile)
	httpMux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	httpMux.HandleFunc("/debug/pprof/trace", pprof.Trace)
	httpMux.HandleFunc("/check", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`{"alive": true}`))
	})

	go func() {
		defer func() {
			if err := recover(); err != nil {
				log.Println("PProf exec recover: ", err)
			}
		}()

		log.Println("server PProf run on: ", port)

		if err := http.ListenAndServe(fmt.Sprintf("0.0.0.0:%d", port), httpMux); err != nil {
			log.Println("PProf listen error: ", err)
		}

	}()

}

func main() {
	ctx1, cancelFn := context.WithCancel(context.Background())
	demo(ctx1)

	// graceful exit
	ch := make(chan os.Signal, 1)
	// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
	// recv signal to exit main goroutine
	// window signal
	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, syscall.SIGHUP)

	// linux signal if you use linux on production,please use this code.
	// signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2, os.Interrupt, syscall.SIGHUP)

	// Block until we receive our signal.
	sig := <-ch

	cancelFn()

	log.Println("exit signal: ", sig.String())
	// Create a deadline to wait for.
	ctx, cancel := context.WithTimeout(context.Background(), wait)
	defer cancel()

	<-ctx.Done()

	log.Println("services shutting down")
}

type waitGroup struct {
	sync.WaitGroup
}

func (w *waitGroup) Wrap(fn func()) {
	w.Add(1)
	go func() {
		fn()
		w.Done()
	}()
}

func (w *waitGroup) WrapRecover(fn func()) {
	w.Add(1)
	go func() {
		defer func() {
			if err := recover(); err != nil {
				log.Println("exec recover: ", err)
			}
		}()

		fn()
		w.Done()
	}()
}

func getData(i int64) string {
	return strconv.FormatInt(i, 10)
}

func demo(ctx context.Context) error {
	go func() {
		ticker := time.NewTicker(500 * time.Millisecond)
		defer ticker.Stop()

		var i int64
		var wg = &waitGroup{}
		for {
			select {
			case <-ticker.C:
				data := getData(i)
				i++
				wg.Wrap(func() {
					log.Println("data: ", data)
				})
			case <-ctx.Done():
				wg.Wait() // when the program exits, call the wait method here 
				return
			}
		}
	}()

	return nil
}

When I start this demo func in the web service, the ctx1 of this demo has not been cancelled. Here we use the wrap method as a callback function. The internal waitgroup is only doing Add(1), Done() operations, please pay attention to me The way it is used in this way. When I was doing pprof analysis, I found that sync block and blocking syscall are more serious indicators. Is there a problem when I use waitgroup like this?
In a concurrent scenario, will the mixed use of waitgroup and goroutine cause the program to be abnormal or hang up?

The short answer would be “no if you use them properly”. However, it would take lot of time to analyze your code above.