تسلط بر همزمانی در Go با استفاده از کانالها
من زمانی یک pipeline پردازش تصویر در Go ساختم.
برای هر مرحله از goroutineها استفاده کردم. آنها را با کانالها به هم متصل کردم. دکمه اجرا را زدم.
برنامه هنگ کرد. مصرف حافظه بالا رفت. سپس از کار افتاد. هیچ خطایی وجود نداشت. فقط سکوت بود.
ساعتها وقت صرف عیبیابی یک scheduler خراب کردم. اشتباه میکردم. من نمیدانستم کانالها چگونه کار میکنند.
در اینجا سه قانون برای جلوگیری از هنگ کردن برنامههای Go آورده شده است.
۱. کانالهای Nil مانند سیاهچالهها هستند یک کانال nil برای همیشه مسدود میماند. اگر روی یک کانال nil دادهای بفرستید یا دریافت کنید، goroutine در همانجا گیر میکند.
- همیشه کانالهای خود را با
makeمقداردهی اولیه کنید. - اگر مطمئن نیستید، از بررسی nil استفاده کنید.
۲. قوانین بستن کانال از panic جلوگیری میکنند بستن یک کانال، دائمی است.
- فقط فرستنده باید کانال را ببندد.
- ارسال داده به یک کانال بسته شده باعث بروز panic میشود.
- از مقدار بازگشتی دوم برای بررسی بسته بودن کانال استفاده کنید:
v, ok := <-ch. - اگر
okبرابر با false باشد، کانال بسته شده است.
۳. برای امنیت بیشتر، از جهتهای کانال استفاده کنید Go به شما اجازه میدهد مشخص کنید که یک کانال برای ارسال است یا دریافت.
chan<- intیعنی فقط میتوانید داده ارسال کنید.<-chan intیعنی فقط میتوانید داده دریافت کنید.- این کار کامپایلر را مجبور میکند تا اشتباهات را قبل از اجرای کد شما شناسایی کند.
چگونه یک pipeline تمیز بسازیم:
- از
defer closeاستفاده کنید تا مطمئن شوید یک کانال دقیقاً یک بار بسته میشود. - از
rangeبرای پیمایش روی کانالها استفاده کنید. این کار وقتی کانال بسته شود، به طور خودکار متوقف میشود. - برای ایجاد قراردادهای دقیق، انواع جهت (direction types) را به توابع خود اختصاص دهید.
وقتی از این الگوها پیروی میکنید، از نشت (leaks) و panic جلوگیری میکنید. شما سیستمهایی میسازید که تست کردن آنها آسان و مستحکم (robust) است.
چالش شما:
تابعی بنویسید که چندین کانال ورودی را در یک کانال خروجی ادغام (merge) کند. از یک حلقه select استفاده کنید. یک timeout ۵۰۰ میلیثانیهای اضافه کنید. مطمئن شوید که هیچ goroutine ای نشت نمیکند.
راه حل خود را در کامنتها قرار دهید.