ویرگول
ورودثبت نام
Roham
Rohamپیدا کردن خودم توی نوشته ها برام جذابه بهم یه حس خوب بودن می ده مخصوصا وقتی با طعم علم و خلاقیت همراه باشه هر روز که بیشتر با این دنیا آشنا می شم حس بهتری برای بیشتر دونستن و یاد دادن درونم رشد می کنه
Roham
Roham
خواندن ۳ دقیقه·۲ ماه پیش

Goroutine در Golang: معماری همزمانی سبک، سریع و بهینه

Goroutine در Golang
Goroutine در Golang

زبان Go از ابتدا با هدف ساده‌سازی ساخت نرم‌افزارهای مقیاس‌پذیر و پرکاربرد طراحی شد. یکی از مهم‌ترین مؤلفه‌هایی که این زبان را از سایر زبان‌ها متمایز می‌کند، Goroutine است؛ سازوکاری بسیار سبک‌وزن برای اجرای همزمان (Concurrency) که به شکل مستقیم توسط runtime خود Go مدیریت می‌شود.

در این مقاله، به ساختار Goroutine، نحوه اجرای آن در runtime، رابطه‌اش با Scheduler، تفاوت آن با Threadهای سیستم‌عامل و نکات کلیدی در استفاده حرفه‌ای از آن می‌پردازیم.


Goroutine چیست؟

گوروتین یک واحد اجرای همزمان در Go است که به‌صورت بسیار سبک‌وزن ایجاد و مدیریت می‌شود. برخلاف Threadهای معمولی سیستم‌عامل که ایجاد آن‌ها هزینه بالایی دارد (Memory + Context Switching)، گوروتین‌ها با مصرف حافظه بسیار کم (در حد چند کیلوبایت) ساخته می‌شوند و در صورت نیاز به‌طور خودکار رشد یا کاهش می‌یابند.

به طور ساده:

  • Thread = سنگین، هزینه‌بر، وابسته به سیستم‌عامل

  • Goroutine = سبک، سریع، مدیریت‌شده توسط Go runtime


معماری اجرای گوروتین در Go Runtime (M:N Scheduler)

Go از یک معماری M:N Scheduler استفاده می‌کند که در آن:

  • M = تعداد Threadهای سیستم‌عامل

  • N = تعداد Goroutineها

Scheduler گوروتین‌ها را روی Threadهای سیستم‌عامل قرار می‌دهد و بدون دخالت برنامه‌نویس، عملیات زمان‌بندی، تعویض context و مدیریت دوره‌ای را تنظیم می‌کند.

سه مفهوم کلیدی در Scheduler عبارت‌اند از:

1. G – Goroutine

کدی که باید اجرا شود.

2. M – Machine

Thread سیستم‌عاملی که واقعاً اجرا را انجام می‌دهد.

3. P – Processor

منبع منطقی برای اجرای Goroutineها که شامل Run Queue است.

وجود Processorها به runtime کمک می‌کند تا load balancing و work stealing را بهتر مدیریت کند.


نحوه ساخت یک Goroutine

ساخت یک Goroutine بسیار ساده است؛ تنها کافی است قبل از فراخوانی تابع، کلمه کلیدی go را قرار دهید:

func main() { go doSomething() println("Main finished") } func doSomething() { println("Hello from goroutine") }

نکات:

  • اجرای main بلافاصله ادامه پیدا می‌کند.

  • اگر main تمام شود، تمام گوروتین‌های دیگر نیز خاتمه می‌یابند.

  • مدیریت همزمانی بین گوروتین‌ها نیازمند سازوکارهایی مانند Channel یا sync است.


اندازه اولیه Stack در Goroutine

برخلاف Threadها که معمولاً Stack با حجم 1MB یا بیشتر دارند، گوروتین‌ها با یک Stack کوچک و داینامیک (حدود 2KB) شروع می‌شوند.
زمانی که حجم Stack مورد نیاز بیشتر شود، runtime آن را دوبرابر می‌کند. این کاهش هزینه اولیه و مدیریت پویا، دلیل اصلی سبک‌وزنی گوروتین‌هاست.


تفاوت میان Goroutine و Thread

تفاوت میان Goroutine و Thread
تفاوت میان Goroutine و Thread

در عمل، می‌توان میلیون‌ها گوروتین را روی چند Thread سیستم‌عامل اجرا کرد.


ارتباط Goroutineها با Channels

برای هماهنگ‌سازی و انتقال داده بین گوروتین‌ها، زبان Go از سازوکاری قدرتمند به نام Channel استفاده می‌کند که الگوی CSP (Communicating Sequential Processes) را پیاده‌سازی می‌کند.

نمونه ساده:

func main() { ch := make(chan string) go func() { ch <- "message from goroutine" }() msg := <-ch println(msg) }

Best Practices در استفاده از Goroutine

1. نشتی گوروتین (Goroutine Leak) را جدی بگیرید

اگر گوروتین منتظر یک channel است اما هیچ‌گاه داده‌ای به آن ارسال نمی‌شود، هرگز متوقف نخواهد شد.
از context‌ها یا timeoutها برای مدیریت طول عمر گوروتین استفاده کنید.

2. از WaitGroup برای همگام‌سازی استفاده کنید

این روش استاندارد برای صبر کردن جهت پایان گوروتین‌هاست.

var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() doWork() }() wg.Wait()

3. از تعداد بیش از حد گوروتین خودداری کنید

اگرچه گوروتین‌ها سبک هستند، اما بی‌برنامه ایجاد کردن تعداد زیاد، ممکن است باعث فشار روی حافظه و Scheduler شود.

4. از Worker Pool برای بارهای سنگین استفاده کنید

برای جلوگیری از تولید بیش از حد گوروتین، الگوی Worker Pool را به‌کار ببرید.


خطاهای رایج هنگام استفاده از Goroutine

  • استفاده از مقادیر loop variable در گوروتین‌ها

  • فراموش کردن close کردن channelها

  • متوقف نشدن گوروتین‌های بلاک‌شده

  • عدم مدیریت context در سرویس‌های شبکه‌ای


نتیجه‌گیری

Goroutine یکی از کلیدی‌ترین ویژگی‌های زبان Go است که امکان ساخت اپلیکیشن‌های مقیاس‌پذیر، سریع و همزمان را با هزینه بسیار کم فراهم می‌کند. معماری scheduler سبک، مدیریت خودکار Stack، و یکپارچگی با Channelها باعث شده Go یکی از بهترین انتخاب‌ها برای سیستم‌های توزیع‌شده، سرویس‌های وب، پردازش همزمان و میکروسرویس‌ها باشد.

goconcurrency
۰
۰
Roham
Roham
پیدا کردن خودم توی نوشته ها برام جذابه بهم یه حس خوب بودن می ده مخصوصا وقتی با طعم علم و خلاقیت همراه باشه هر روز که بیشتر با این دنیا آشنا می شم حس بهتری برای بیشتر دونستن و یاد دادن درونم رشد می کنه
شاید از این پست‌ها خوشتان بیاید