برنامه نویسی

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

شرح تصویر

Leapcell: پلت فرم بدون سرور نسل بعدی برای میزبانی وب ، کارهای ASYNC و REDIS

errgroup یک ابزار در کتابخانه رسمی Go است x برای اجرای همزمان چندگانه استفاده می شود goroutines و رسیدگی به خطاها آن را پیاده سازی می کند errgroup.Group بر sync.WaitGroup، ارائه توابع قدرتمندتر برای برنامه نویسی همزمان.

مزایای errgroup

در مقایسه با sync.WaitGroupبا errgroup.Group مزایای زیر را دارد:

  1. رسیدگی به خطا: sync.WaitGroup فقط مسئول انتظار برای goroutines برای تکمیل و رسیدگی به مقادیر یا خطاهای برگشتی. در حالی که errgroup.Group نمی تواند به طور مستقیم مقادیر بازگشت را کنترل کند ، می تواند بلافاصله سایر دویدن را لغو کند goroutines وقتی الف goroutine با یک خطا روبرو می شود و اولین غیرnil خطا در Wait روش
  2. زمینه لغو: errgroup می تواند در رابطه با context.Contextبشر وقتی الف goroutine با یک خطا روبرو می شود ، می تواند به طور خودکار دیگران را لغو کند goroutines، کنترل موثر منابع و جلوگیری از کار غیر ضروری.
  3. ساده سازی برنامه نویسی همزمان: با استفاده از errgroup می تواند کد BoilerPlate را برای کنترل خطا کاهش دهد. توسعه دهندگان نیازی به مدیریت دستی حالتهای خطا و منطق همگام سازی ندارند و برنامه نویسی همزمان را ساده تر و حفظ می کنند.
  4. محدود کردن تعداد همزمانی: 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

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا