9.4.1 الگو Wait For Result

9.4.1 الگو Wait For Result

9.4.1.1 توضیحات #

الگوی Wait For Result یکی از پرکاربردترین الگوها در برنامه‌نویسی همزمان با Go است که هدفش اجرای عملیات به صورت goroutine و انتظار برای دریافت نتیجه از طریق channel است. در این الگو، معمولاً یک کانال تعریف می‌شود تا داده یا نتیجه (و حتی خطا) از goroutine به کد اصلی منتقل شود. این کار باعث می‌شود عملیات‌های طولانی یا زمان‌بر (مثل خواندن فایل، تماس با API یا انجام محاسبات سنگین) بدون بلاک کردن کل برنامه اجرا شوند و به محض آماده شدن نتیجه، به صورت ایمن و همزمان، دریافت شوند. ساختار معمول این الگو به این صورت است که یک goroutine کار را انجام می‌دهد و در پایان نتیجه را داخل کانال می‌فرستد؛ در این مدت goroutine اصلی (یا هر مصرف‌کننده دیگر) با دریافت روی کانال منتظر نتیجه می‌ماند.

این الگو هم از نظر سادگی و هم از نظر ایمنی، مزیت بالایی دارد و پیاده‌سازی آن با استفاده از کانال‌های Go، باعث می‌شود برنامه دچار race condition یا مشکلات همزمانی نشود. همچنین با اضافه کردن ساختارهایی مثل struct حاوی مقدار و خطا، یا استفاده از context برای مدیریت تایم‌اوت و کنسل کردن، می‌توان این الگو را کاملاً production-ready کرد. به طور خلاصه، Wait For Result راهکاری است که به کمک آن، ضمن استفاده بهینه از منابع و افزایش کارایی، کنترل کاملی بر زمان و نتیجه عملیات‌های همزمان خواهید داشت و به راحتی می‌توانید منطق‌های پیچیده‌تر مثل جمع‌آوری نتایج، مدیریت خطاها، یا پیاده‌سازی تایم‌اوت را نیز به آن اضافه کنید.

9.4.1.2 دیاگرام #

sequenceDiagram participant Main as Main Goroutine participant Chan as Channel participant Worker as Worker Goroutine Main->>Chan: ایجاد channel Main->>Worker: راه‌اندازی goroutine (با ارجاع به channel) Worker->>Worker: انجام عملیات (مثلاً I/O) Worker->>Chan: ارسال نتیجه به channel Main->>Chan: منتظر دریافت نتیجه از channel Chan-->>Main: دریافت نتیجه

9.4.1.3 نمونه کد #

 1package main
 2
 3import (
 4	"fmt"
 5	"time"
 6)
 7
 8func longRunningTask(c chan int) {
 9	time.Sleep(3 * time.Second)
10	c <- 42
11}
12
13func main() {
14	c := make(chan int)
15	go longRunningTask(c)
16
17	result := <-c
18	fmt.Println("Result:", result)
19}
1$ go run main.go
2Result: 42

این کد نمونه، مفهوم الگوی Wait For Result را در Go به شکلی بسیار ساده و شفاف پیاده‌سازی می‌کند. در این برنامه، یک تابع به نام longRunningTask داریم که شبیه‌ساز یک کار زمان‌بر است؛ این تابع پس از سه ثانیه توقف (با استفاده از time.Sleep) عدد ۴۲ را از طریق یک کانال (channel) به بخش اصلی برنامه (main goroutine) ارسال می‌کند. در تابع main، ابتدا یک کانال بدون بافر ساخته شده و سپس با استفاده از goroutine، تابع زمان‌بر به طور موازی اجرا می‌شود. پس از آن، برنامه اصلی منتظر می‌ماند تا مقدار از کانال دریافت شود و به محض دریافت، مقدار دریافت‌شده را چاپ می‌کند.

کاربرد این الگو در سناریوهایی است که نیاز داریم عملیات زمان‌بر یا همزمان را اجرا کنیم و در عین حال تا زمان آماده شدن نتیجه، سایر بخش‌های برنامه بلاک نشود یا بتوانیم همزمان چندین کار دیگر را انجام دهیم. دریافت مقدار از کانال در اینجا نقش “منتظر ماندن برای نتیجه” را دارد و هنگامی که goroutine مقدار را ارسال کند، ادامه‌ی برنامه اصلی اجرا می‌شود. این روش به صورت idiomatic و ایمن، همزمانی و انتقال داده بین goroutineها را در Go مدیریت می‌کند و به‌سادگی می‌توان آن را در مسائل واقعی‌تر، مثلاً پردازش موازی درخواست‌ها یا جمع‌آوری نتایج عملیات‌های موازی، استفاده کرد.

9.4.1.4 کاربردها #

  • Web Scraping: زمانی که عملیات web scraping انجام می‌دهید، معمولاً نیاز است به طور همزمان از چندین آدرس وب داده جمع‌آوری کنید. در این حالت می‌توانید درخواست‌ها را به صورت موازی (با استفاده از goroutine) به چندین سایت ارسال کرده و سپس با استفاده از این الگو منتظر بمانید تا نتایج همه درخواست‌ها دریافت شود؛ این کار باعث افزایش چشمگیر سرعت جمع‌آوری داده می‌شود.
  • API Calls: در معماری میکروسرویس، گاهی لازم است چندین API را به صورت همزمان فراخوانی کنید و پس از دریافت نتایج، نتیجه نهایی را به کلاینت برگردانید. این الگو به شما این امکان را می‌دهد که همزمان درخواست‌ها را ارسال کنید و به صورت منتظر (blocking) روی دریافت پاسخ‌ها بمانید تا همه نتایج آماده شود و در نهایت با کمترین زمان ممکن پاسخ‌دهی انجام دهید.
  • Parallel Computation: در پردازش‌های علمی یا داده‌ای، معمولاً نیاز است محاسبات سنگین را به صورت موازی اجرا کنید. می‌توانید هر بخش از محاسبات را در goroutine جداگانه انجام دهید و با استفاده از این الگو، تا زمانی که تمام نتایج آماده نشده‌اند، منتظر بمانید و پس از دریافت همه پاسخ‌ها، مرحله بعدی پردازش را آغاز کنید.