errgroup: باز کردن قفل قدرت همزمانی Go

Leapcell: پلت فرم بدون سرور نسل بعدی برای میزبانی وب ، کارهای ASYNC و REDIS
errgroup
یک ابزار در کتابخانه رسمی Go است x
برای اجرای همزمان چندگانه استفاده می شود goroutines
و رسیدگی به خطاها آن را پیاده سازی می کند errgroup.Group
بر sync.WaitGroup
، ارائه توابع قدرتمندتر برای برنامه نویسی همزمان.
مزایای errgroup
در مقایسه با sync.WaitGroup
با errgroup.Group
مزایای زیر را دارد:
-
رسیدگی به خطا:
sync.WaitGroup
فقط مسئول انتظار برایgoroutines
برای تکمیل و رسیدگی به مقادیر یا خطاهای برگشتی. در حالی کهerrgroup.Group
نمی تواند به طور مستقیم مقادیر بازگشت را کنترل کند ، می تواند بلافاصله سایر دویدن را لغو کندgoroutines
وقتی الفgoroutine
با یک خطا روبرو می شود و اولین غیرnil
خطا درWait
روش -
زمینه لغو:
errgroup
می تواند در رابطه باcontext.Context
بشر وقتی الفgoroutine
با یک خطا روبرو می شود ، می تواند به طور خودکار دیگران را لغو کندgoroutines
، کنترل موثر منابع و جلوگیری از کار غیر ضروری. -
ساده سازی برنامه نویسی همزمان: با استفاده از
errgroup
می تواند کد BoilerPlate را برای کنترل خطا کاهش دهد. توسعه دهندگان نیازی به مدیریت دستی حالتهای خطا و منطق همگام سازی ندارند و برنامه نویسی همزمان را ساده تر و حفظ می کنند. -
محدود کردن تعداد همزمانی:
errgroup
یک رابط برای محدود کردن تعداد همزمان فراهم می کندgoroutines
برای جلوگیری از اضافه بار ، که ویژگی ای استsync.WaitGroup
ندارد
مثال استفاده از Sync.WaitGroup
قبل از معرفی errgroup.Group
، بیایید ابتدا استفاده از آن را مرور کنیم sync.WaitGroup
بشر
package main
import (
"fmt"
"net/http"
"sync"
)
func main() {
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
var err error
var wg sync.WaitGroup
for _, url := range urls {
wg.Add(1)
go func() {
defer wg.Done()
resp, e := http.Get(url)
if e != nil {
err = e
return
}
defer resp.Body.Close()
fmt.Printf("fetch url %s status %s\n", url, resp.Status)
}()
}
wg.Wait()
if err != nil {
fmt.Printf("Error: %s\n", err)
}
}
نتیجه اجرای:
$ go run examples/main.go
fetch url http://www.google.com/ status 200 OK
fetch url http://www.golang.org/ status 200 OK
Error: Get "http://www.somestupidname.com/": dial tcp: lookup www.somestupidname.com: no such host
اصطلاح معمولی از sync.WaitGroup
:
var wg sync.WaitGroup
for ... {
wg.Add(1)
go func() {
defer wg.Done()
// do something
}()
}
wg.Wait()
مثال استفاده از errgroup.group
استفاده اساسی
الگوی استفاده از errgroup.Group
شبیه به آن است sync.WaitGroup
بشر
package main
import (
"fmt"
"net/http"
"golang.org/x/sync/errgroup"
)
func main() {
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
var g errgroup.Group
for _, url := range urls {
g.Go(func() error {
resp, err := http.Get(url)
if err != nil {
return err
}
defer resp.Body.Close()
fmt.Printf("fetch url %s status %s\n", url, resp.Status)
return nil
})
}
if err := g.Wait(); err != nil {
fmt.Printf("Error: %s\n", err)
}
}
نتیجه اجرای:
$ go run examples/main.go
fetch url http://www.google.com/ status 200 OK
fetch url http://www.golang.org/ status 200 OK
Error: Get "http://www.somestupidname.com/": dial tcp: lookup www.somestupidname.com: no such host
زمینه لغو
errgroup
تصویب کردن errgroup.WithContext
برای اضافه کردن یک عملکرد لغو.
package main
import (
"context"
"fmt"
"net/http"
"sync"
"golang.org/x/sync/errgroup"
)
func main() {
var urls = []string{
"http://www.golang.org/",
"http://www.google.com/",
"http://www.somestupidname.com/",
}
g, ctx := errgroup.WithContext(context.Background())
var result sync.Map
for _, url := range urls {
g.Go(func() error {
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
if err != nil {
return err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
result.Store(url, resp.Status)
return nil
})
}
if err := g.Wait(); err != nil {
fmt.Println("Error: ", err)
}
result.Range(func(key, value any) bool {
fmt.Printf("fetch url %s status %s\n", key, value)
return true
})
}
نتیجه اجرای:
$ go run examples/withcontext/main.go
Error: Get "http://www.somestupidname.com/": dial tcp: lookup www.somestupidname.com: no such host
fetch url http://www.google.com/ status 200 OK
از آنجا که درخواست به http://www.somestupidname.com/ خطایی را گزارش داد ، این برنامه درخواست را به http://www.golang.org/ لغو کرد.
محدود کردن تعداد همزمانی
errgroup
تصویب کردن errgroup.SetLimit
برای محدود کردن تعداد اجرای همزمان goroutines
بشر
package main
import (
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
var g errgroup.Group
g.SetLimit(3)
for i := 1; i <= 10; i++ {
g.Go(func() error {
fmt.Printf("Goroutine %d is starting\n", i)
time.Sleep(2 * time.Second)
fmt.Printf("Goroutine %d is done\n", i)
return nil
})
}
if err := g.Wait(); err != nil {
fmt.Printf("Encountered an error: %v\n", err)
}
fmt.Println("All goroutines complete.")
}
نتیجه اجرای:
$ go run examples/main.go
Goroutine 3 is starting
Goroutine 1 is starting
Goroutine 2 is starting
Goroutine 2 is done
Goroutine 1 is done
Goroutine 5 is starting
Goroutine 3 is done
Goroutine 6 is starting
Goroutine 4 is starting
Goroutine 6 is done
Goroutine 5 is done
Goroutine 8 is starting
Goroutine 4 is done
Goroutine 7 is starting
Goroutine 9 is starting
Goroutine 9 is done
Goroutine 8 is done
Goroutine 10 is starting
Goroutine 7 is done
Goroutine 10 is done
All goroutines complete.
سعی کنید شروع کنید
errgroup
تصویب کردن errgroup.TryGo
برای شروع کار ، که باید در رابطه با آن استفاده شود errgroup.SetLimit
بشر
package main
import (
"fmt"
"time"
"golang.org/x/sync/errgroup"
)
func main() {
var g errgroup.Group
g.SetLimit(3)
for i := 1; i <= 10; i++ {
if g.TryGo(func() error {
fmt.Printf("Goroutine %d is starting\n", i)
time.Sleep(2 * time.Second)
fmt.Printf("Goroutine %d is done\n", i)
return nil
}) {
fmt.Printf("Goroutine %d started successfully\n", i)
} else {
fmt.Printf("Goroutine %d could not start (limit reached)\n", i)
}
}
if err := g.Wait(); err != nil {
fmt.Printf("Encountered an error: %v\n", err)
}
fmt.Println("All goroutines complete.")
}
نتیجه اجرای:
$ go run examples/main.go
Goroutine 1 started successfully
Goroutine 1 is starting
Goroutine 2 is starting
Goroutine 2 started successfully
Goroutine 3 started successfully
Goroutine 4 could not start (limit reached)
Goroutine 5 could not start (limit reached)
Goroutine 6 could not start (limit reached)
Goroutine 7 could not start (limit reached)
Goroutine 8 could not start (limit reached)
Goroutine 9 could not start (limit reached)
Goroutine 10 could not start (limit reached)
Goroutine 3 is starting
Goroutine 2 is done
Goroutine 3 is done
Goroutine 1 is done
All goroutines complete.
تفسیر کد منبع
کد منبع از errgroup
عمدتا از 3 پرونده تشکیل شده است:
ساختار اصلی
type token struct{}
type Group struct {
cancel func(error)
wg sync.WaitGroup
sem chan token
errOnce sync.Once
err error
}
-
token
: یک ساختار خالی که برای انتقال سیگنال ها برای کنترل تعداد همزمانی استفاده می شود. -
Group
:-
cancel
: تابعی که هنگام لغو متن خوانده می شود. -
wg
: داخلی استفاده می شودsync.WaitGroup
بشر -
sem
: کانال سیگنال که تعداد Coroutines همزمان را کنترل می کند. -
errOnce
: اطمینان حاصل می کند که خطا فقط یک بار رسیدگی می شود. -
err
: خطای اول را ثبت می کند.
-
روش های اصلی
- تنظیم: تعداد همزمانی را محدود می کند.
func (g *Group) SetLimit(n int) {
if n < 0 {
g.sem = nil
return
}
if len(g.sem) != 0 {
panic(fmt.Errorf("errgroup: modify limit while %v goroutines in the group are still active", len(g.sem)))
}
g.sem = make(chan token, n)
}
- رفتن: برای اجرای کار یک Coroutine جدید را شروع می کند.
func (g *Group) Go(f func() error) {
if g.sem != nil {
g.sem <- token{}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
}
- انتظار داشتن: منتظر تکمیل همه کارها و بازگشت خطای اول است.
func (g *Group) Wait() error {
g.wg.Wait()
if g.cancel != nil {
g.cancel(g.err)
}
return g.err
}
- کوشش: سعی می کند یک کار را شروع کند.
func (g *Group) TryGo(f func() error) bool {
if g.sem != nil {
select {
case g.sem <- token{}:
default:
return false
}
}
g.wg.Add(1)
go func() {
defer g.done()
if err := f(); err != nil {
g.errOnce.Do(func() {
g.err = err
if g.cancel != nil {
g.cancel(g.err)
}
})
}
}()
return true
}
پایان
errgroup
یک کتابخانه رسمی رسمی است که بر اساس آن قابلیت های کنترل خطا را اضافه می کند sync.WaitGroup
، ارائه توابعی مانند همگام سازی ، انتشار خطا و لغو متن. آن را WithContext
روش می تواند یک تابع لغو اضافه کند ، SetLimit
می تواند تعداد همزمانی را محدود کند ، و TryGo
می تواند سعی کند یک کار را شروع کند. کد منبع به طور مبتکرانه طراحی و شایسته مرجع است.
سرانجام ، من می خواهم مناسب ترین بستر را برای استقرار Golang توصیه کنم: جهش
1. پشتیبانی چند زبانه
- با JavaScript ، Python ، Go یا Rust توسعه دهید.
5. پروژه های نامحدود را به صورت رایگان مستقر کنید
- فقط برای استفاده پرداخت کنید – بدون درخواست ، بدون هزینه.
3. راندمان هزینه بی نظیر
- پرداخت به عنوان شما بدون هیچ گونه هزینه بیکار.
- مثال: 25 دلار از درخواست های 6.94M در زمان پاسخ متوسط 60ms پشتیبانی می کند.
4. تجربه توسعه دهنده ساده
- UI بصری برای راه اندازی بی دردسر.
- خطوط لوله CI/CD کاملاً خودکار و ادغام GITOPS.
- معیارهای زمان واقعی و ورود به سیستم برای بینش های عملی.
5. مقیاس پذیری بی دردسر و عملکرد بالا
- مقیاس خودکار برای رسیدگی به همزمانی بالا با سهولت.
- صفر سربار عملیاتی – فقط روی ساختمان تمرکز کنید.
در اسناد بیشتر کاوش کنید!
توییتر Leapcell: https://x.com/leapcellhq