برنامه نویسی

شرایط مسابقه در Go: یک آموزش ساده

سلام من Shrijith Venkatrama ، بنیانگذار Hexmos هستم. در حال حاضر ، من در حال ساختن LiveApi هستم ، ابزاری که تولید اسناد API را از کد شما به طرز مسخره ای آسان می کند.

به تازگی ، من در برنامه GO خود با چند شرایط مسابقه سر و کار داشته ام که بسیاری از گوروس ها به طور همزمان از بین می روند.

در زمینه های عملی – شرایط مسابقه معمولاً هنگامی اتفاق می افتد که چندین گوروتین به طور همزمان به دسترسی و تغییر داده های مشترک ، منجر به نتایج غیرقابل پیش بینی می شوند.

این آموزش شما را از اصول اولیه گرفته تا نمونه های پیچیده تری از شرایط مسابقه در Go سوق می دهد.

ما از مثالهای ساده و قابل اجرا استفاده خواهیم کرد تا نشان دهیم چه اشتباهی رخ می دهد و چگونه می توان این مسائل را مشاهده کرد.

توجه داشته باشید که من عمدتاً روی سناریوهای معمولی عملی متمرکز شده ام که در آن شرایط مسابقه پدیدار می شود – و نه یک پوشش جامع از امکانات.

شرایط مسابقه چیست؟

یک وضعیت مسابقه زمانی اتفاق می افتد که نتیجه یک برنامه به زمان گوروتین ها بستگی داشته باشد.

اگر دو یا چند گوروتین سعی کنید بدون کنترل همان متغیر را بخوانید و بنویسید ، رفتار حشره ای دریافت می کنید.

بیایید با یک مثال اساسی شروع کنیم که در آن وضعیت مسابقه هنوز اتفاق نمی افتد:

    package main

    import "fmt"

    func main() {
        counter := 0
        counter++
        fmt.Println("Counter:", counter)
    }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خروجی:

Counter: 1

در اینجا ، هیچ شرایط مسابقه ای وجود ندارد زیرا ما فقط از یک goroutine استفاده می کنیم ( main عملکرد).

متغیر counter با خیال راحت افزایش می یابد اما وقتی ما گوروها را اضافه می کنیم ، همه چیز می تواند اشتباه شود.

معرفی goroutines و یک شرایط مسابقه ساده

حال ، بیایید Goroutines را برای افزایش یک متغیر مشترک اضافه کنیم. اینجاست که شرایط مسابقه ظاهر می شود.

    package main

    import (
        "fmt"
        "time"
    )

    func increment(counter *int) {
        *counter++
    }

    func main() {
        counter := 0
        go increment(&counter) // First goroutine
        go increment(&counter) // Second goroutine

        time.Sleep(time.Second) // Wait for goroutines to finish
        fmt.Println("Counter:", counter)
    }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خروجی (ممکن است متفاوت باشد):

Counter: 1 یا Counter: 2

چه اتفاقی می افتد؟

انتظار داریم counter 2 (دو افزایش) ، اما گاهی اوقات 1 است.

چرا؟ Goroutines برای به روزرسانی مسابقه می دهند counterبشر

ممکن است کسی تغییر دیگری را بازنویسی کند زیرا هیچ هماهنگی وجود ندارد.

این یک شرایط مسابقه است.

این کار را چندین بار اجرا کنید – نتایج متفاوتی را مشاهده خواهید کرد. این غیرقابل پیش بینی بودن یک مسابقه است!

بخش 3: دیدن شرایط مسابقه در عمل

بیایید آن را با goroutines بیشتر مقیاس کنیم تا مشکل واضح تر شود:

    package main

    import (
        "fmt"
        "time"
    )

    func increment(counter *int) {
        *counter++
    }

    func main() {
        counter := 0
        for i := 0; i < 100; i++ {
            go increment(&counter)
        }

        time.Sleep(time.Second)
        fmt.Println("Counter:", counter)
    }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خروجی (ممکن است متفاوت باشد):

Counter: 97 یا Counter: 88 یا چیز دیگری زیر 100

ما 100 goroutine را برای افزایش راه اندازی کردیم counter، بنابراین باید 100 باشد ، درست است؟ اما تقریباً همیشه کمتر است. هر گوروتین می خواند و می نویسد counter در عین حال ، و برخی به روزرسانی ها از بین می روند. به عنوان مثال:

  • goroutine 1 می خواند counter = 5، آن را به 6 افزایش می دهد.
  • goroutine 2 می خواند counter = 5 (قبل از نوشتن Goroutine 1) ، آن را به 6 افزایش می دهد.
  • هر دو دوباره 6 را می نویسند ، یک افزایش را از دست می دهند.

این همپوشانی قلب یک وضعیت مسابقه است.

تشخیص شرایط مسابقه با ردیاب مسابقه GO

GO یک ابزار داخلی برای مشاهده شرایط مسابقه دارد. برنامه را با -race پرچم 🙂 پرچم 🙂

    go run -race yourfile.go
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

بیایید از آن در آخرین مثال ما استفاده کنیم:

    package main

    import (
        "fmt"
        "time"
    )

    func increment(counter *int) {
        *counter++
    }

    func main() {
        counter := 0
        for i := 0; i < 100; i++ {
            go increment(&counter)
        }

        time.Sleep(time.Second)
        fmt.Println("Counter:", counter)
    }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

وقتی دویدید go run -race main.go، یک هشدار مانند:

    WARNING: DATA RACE
    Read at 0x00c0000a4010 by goroutine 6:
      main.increment()
          /path/to/main.go:9 +0x44

    Previous write at 0x00c0000a4010 by goroutine 5:
      main.increment()
          /path/to/main.go:9 +0x54
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

این به ما می گوید که دو گوروتین در حال جمع شدن هستند counterبشر ردیاب مسابقه مشکل را برطرف نمی کند – فقط به شما کمک می کند تا آن را پیدا کنید.

رفع شرایط مسابقه (پیش نمایش)

برای رفع شرایط مسابقه ، اغلب به هماهنگ سازی نیاز دارید. Go ابزارهایی مانند sync.Mutexبشر در اینجا چگونه می توانیم آخرین مثال را اصلاح کنیم:

    package main

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

    func increment(counter *int, mu *sync.Mutex) {
        mu.Lock() // Lock before changing counter
        for i := 0; i < 10; i++ {
            *counter++
        }
        mu.Unlock() // Unlock after
    }

    func main() {
        counter := 0
        var mu sync.Mutex
        for i := 0; i < 5; i++ {
            go increment(&counter, &mu)
        }

        time.Sleep(time.Second)
        fmt.Println("Counter:", counter)
    }
حالت تمام صفحه را وارد کنید

از حالت تمام صفحه خارج شوید

خروجی:

Counter: 50

حالا همیشه 50 است!

در Mutex (کوتاه برای محرومیت متقابل) فقط یک به روزرسانی گوروتین را تضمین می کند counter در یک زمان

دیگر شرایط مسابقه نیست.

ما در یک آموزش دیگر عمیق تر به اصلاحات شیرجه می زنیم ، اما این ایده اصلی را نشان می دهد.

پیچیدن

شرایط مسابقه هنگامی اتفاق می افتد که Goroutines با داده های مشترک بدون قوانین مبارزه می کند. شما دیده اید:

  1. یک برنامه ایمن تک رشته ای.
  2. یک مسابقه ساده با دو گوروتین.
  3. یک مسابقه بزرگتر با 100 گوروتین.
  4. نحوه تشخیص نژادها با -raceبشر
  5. یک نگاه دزدکی حرکت در رفع آن Mutexبشر

خودتان این مثالها را اجرا کنید.

اگر شما قادر به مشاهده شرایط مسابقه نیستید – فقط گورواتین ها/حلقه ها را افزایش دهید – مشکلات به زودی شروع به نمایش می کنند!

اعداد را تغییر دهید ، جوراب های بیشتری اضافه کنید و از ردیاب مسابقه استفاده کنید.

هرچه بیشتر آزمایش کنید ، بهتر خواهید فهمید که چگونه نژادها به کد شما می پردازند!

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

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

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

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