در دنیای برنامهنویسی امروزی، نیاز به همزمانی و انجام چندین کار بهطور همزمان در برنامهها بیش از پیش حس میشه. زبان Go ابزارهای قدرتمندی مثل Goroutines و Channels رو برای انجام عملیات همزمان (concurrent) در اختیار شما قرار میده. در این بخش از آموزش، با این مفاهیم آشنا میشویم و یاد میگیریم چطور با استفاده از این ابزارها برنامههای همزمان در Go بسازیم.
Goroutine بهطور ساده یک وظیفه همزمان در Go است. این وظایف به شما این امکان رو میدهند که کدهای مختلف رو بهطور همزمان اجرا کنید. در واقع، Go به شما این اجازه رو میده که تعداد زیادی از این Goroutineها رو در یک برنامه راهاندازی کنید و کارهای مختلف رو بدون وقفه انجام بدید.
راهاندازی یک Goroutine:
go func() {fmt.Println("Goroutine در حال اجرا است!")}()
در اینجا، با استفاده از کلمهکلیدی go یک Goroutine جدید راهاندازی میکنیم که بهطور همزمان اجرا میشه.
Channelها در Go ابزاری برای انتقال داده بین Goroutineها هستند. با استفاده از Channelها میتوانید دادهها رو بهصورت ایمن و همزمان بین Goroutineها ارسال و دریافت کنید.
ارسال داده به Channel:
ch := make(chan string)go func() {ch <- "سلام از Goroutine!"}()fmt.Println(<-ch) // دریافت داده از Channel
در اینجا، دادهای از یک Goroutine به Channel ارسال میشود و در خط بعدی از Channel دریافت میشود.
در Go دو نوع Channel داریم: Buffered و Unbuffered.
Unbuffered Channel: این نوع Channel دادهها رو بلافاصله از یک Goroutine به دیگری منتقل میکنه و بدون منتظر ماندن نمیتوان دادهای را ارسال یا دریافت کرد.
Buffered Channel: این نوع Channel میتواند تعدادی داده را در خود ذخیره کرده و بعداً ارسال کند. اگر Buffer پر شود، ارسال داده متوقف میشود تا فضای کافی برای ذخیره وجود داشته باشد.
ch := make(chan string, 2) // Channel با ظرفیت 2ch <- "سلام"ch <- "دنیا"fmt.Println(<-ch) // خروجی: سلام
دستور select در Go این امکان رو به شما میده که با چندین Channel بهطور همزمان کار کنید و بر اساس وضعیت Channelها عملیاتهای مختلف انجام بدید.
select {case msg1 := <-ch1:fmt.Println("دریافت پیام از ch1:", msg1)case msg2 := <-ch2:fmt.Println("دریافت پیام از ch2:", msg2)default:fmt.Println("هیچ پیامی دریافت نشده است.")}
در اینجا، Go بسته به اینکه کدوم Channel دادهای برای دریافت داشته باشه، عملیات مربوطه رو انجام میده.
Mutex یک مکانیزم همزمانی در Go است که برای جلوگیری از Race Condition استفاده میشود. Race Condition زمانی رخ میدهد که دو Goroutine بهطور همزمان سعی میکنند به یک داده دسترسی داشته باشند و باعث خطا یا تغییر غیرمنتظره دادهها بشه.
استفاده از Mutex برای جلوگیری از Race Condition:
var mu sync.Mutexmu.Lock()// دسترسی ایمن به دادههاmu.Unlock()
با استفاده از Lock و Unlock میتوانیم از دسترسی همزمان به دادهها جلوگیری کنیم.
WaitGroup به شما این امکان رو میده که منتظر بمانید تا چندین Goroutine تمام بشن. این ابزار برای همگامسازی Goroutineها استفاده میشه.
استفاده از WaitGroup:
var wg sync.WaitGroupwg.Add(1) // تعداد Goroutineهایی که باید منتظرشون بمونیمgo func() {defer wg.Done() // به محض تمام شدن این Goroutine از WaitGroup کم میکنهfmt.Println("Goroutine تمام شد!")}()wg.Wait() // منتظر ماندن تا تمام Goroutineها تمام بشن
Context در Go برای مدیریت زمانهای طولانی و محدود کردن زمان انجام عملیاتهای مختلف استفاده میشود. بهطور مثال، میتوان از آن برای کنترل زمان انقضای درخواستها استفاده کرد.
استفاده از Context:
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)defer cancel()select {case <-time.After(5 * time.Second):fmt.Println("کار تمام شد!")case <-ctx.Done():fmt.Println("درخواست با خطا مواجه شد:", ctx.Err())}
در اینجا، با استفاده از Context، اگر مدت زمان تعیینشده تمام بشه، عملیات متوقف میشه.
در این بخش با مفاهیم همزمانی در Go مانند Goroutineها، Channelها، Mutexها، WaitGroup و Context API آشنا شدیم. با استفاده از این ابزارها، میتونید برنامههای همزمان و مقیاسپذیر بسازید.