چجوری Channel ها در Golang کار میکنن

در این پست میخوایم راجب اینکه channel ها چطور کار میکنن و نحوه ی ارسال/دریافت دیتا روی اون ها به چه شکل هست صحبت کنیم.در گو همزمانی فقط در سینتکس خلاصه نمیشه . بلکه یک الگوی طراحی هست بدلیل اینکه : همزمانی نیاز به همگام سازی دارد.و گو برای رسیدن به این هدف از الگویی به نام CSP استفاده میکنه ( Communicating Sequential Processes) . و فلسفه اصلی همزمانی در گو به این صورت هست که :برای برقراری ارتباط ، حافظه رو به اشتراک نذارید بلکه حافظه رو با برقراری ارتباط به اشتراک بزارید.گوروتین یک thread سبک هست که توسط Go runtime مدیریت میشه و میتونه وظایف را به طور همزمان و به طور بالقوه به صورت موازی نیز اجرا کنه.خب بیاین برای شروع یک buffered channel بسازیم:ch := make(chan int, 3)کد بالا یک Buffered Channel با ظرفیت ۳ و از نوع int میسازه.در عمق ماجرا تابع make یک شی از نوعhchan داخل heap میسازه و یک اشاره گر که به اون اشاره میکنه رو بر میگردونه . یه سری از فیلد های ساختار hchan تو تصویر زیر اومده:فیلد buf : یک اشاره گر به یک ارایه هست که نقش صف را دارد (فقط برای buffered channel استفاده میشود).فیلد sendx : ایندکس عنصر ارسالی در آرایه بافرفیلد recvx : ایندکس عنصر دریافتی در آرایه بافرفیلد lock : باعث میشه مطمئن بشیم که خواندن و نوشتن روی channel بصورت atomic انجام بشهدر ادامه میخوایم سناریو های زیر رو بررسی کنیم:ارسال و دریافت بدون بلاک شدندریافت از یک channel خالیارسال روی channel ای که پر هستارسال و دریافت بدون بلاک شدن:وقتی که channel خالی باشه ما میتونیم آیتم هارو در صف بدون اینکه بلاک بشیم وارد کنیم.ارسال بدون بلاک شدنو همین طور زمانی که channel خالی نباشه ما میتونیم آیتم هارو از جلوی صف بدون اینکه بلاک بشیم دریافت کنیم.دریافت بدون بلاک شدنگوروتین های منتظر:دو فیلد دیگه هست که زمانی که گوروتین های منتظر داریم خیلی برای ما مهم هستن :فیلد recvq : گوروتین های بلاک شده ای که سعی داشتن از channel دیتا بخونن رو نگهداری میکنه.فیلد sendq : گوروتین های بلاک شده ای که سعی داشتن به channel دیتا بفرستند رو نگهداری میکنه.درضمن recvq , sendq از جنس linked list هستن.type hchan struct { buf unsafe.Pointer sendx uint recvx uint lock mutex sendq waitq recvq waitq ... // more fields}type waitq struct { first *sudog last *sudog}// pseudo goroutinetype sudog struct { g *g elem unsafe.Pointer next *sudog prev *sudog ... c *hchan}دریافت از یک channel خالی:زمانی که channel خالی هست عملیات خواندن از اون باعث بلاک شدن اون گوروتین میشه. فراموش نکنید که همه ی گوروتین هایی که ار انتظار دریافت هستن داخل صف recvq قرار میگیرنراستی حواستون باشه که os thread بلاک نمیشه و فقط گوروتین بلاک میشه. خب حالا سوال پیش میاد که این گوروتین بلاک شده کی دوباره شروع به کار میکنه ؟؟؟همون طور که حدس میزنین جوابش میشه وقتی که یه گوروتین دیگه چیزی روی این channel بفرسته!بیاین باهم یه بررسی بکنیم ببینیم چه اتفاق هایی رخ میده:گوروتین جدید دیتا ای رو که میخواد بفرسته رو مستقیم کپی میکنه داخل element اولین گوروتین ای که در صف منتظره !!!!!!!!!!!!!!!!!!اولین گوروتین در حال انتظار از recvq برداشته و از صف حدف میشه.حالا runtime scheduler گوروتین برداشته شده رو داخل runqueue میذاره . بنابرین الان گوروتین بلاک شده آمادست که دوباره اجرا بشه.احتمالا خیلی شوکه شدین از اینکه وقتی بافر خالی باشه گوروتینی که میخواد دیتا رو بفرسته داخل channel مستقیم اون رو کپی می کنه داخل elem گوروتین ای که منتظر هست و اول دیتارو داخل بافر نمی فرستهبرای این انقدر عجیبه که گوروتین ها برای خودشون stack جدا دارن و معمولا هیچ وقت گوروتین ها از stack گوروتین دیگه ای به طور مستقیم نمیخونن و نمی نویسن . فقط در این حالته که گوروتین ها همچین کاری میکنن. و دلیلش هم بهبود پرفورمنس هست.ارسال روی channel ای که پر هستزمانی که channel پر هست عملیات ارسال به اون باعث بلاک شدن گوروتین میشه. ارسال روی channel پرتا وقتی که یک گوروتین دیگه از channel چیزی دریافت کنه بقیه گوروتین ها بلاک شده باقی میمونن.بیاین جزییاتش رو باهم بررسی کنیم:زمانی که یک گوروتین جدید عملیات خوندن از channel رو انجام میده , اولین عنصر موجود در بافر پاک میشهاولین گوروتین منتظر از صف sendq برداشته و حذف میشهمقدار element گوروتین برداشته شده داخل بافر کپی میشهحالا runtime scheduler گوروتین برداشته شده رو داخل runqueue میذاره . بنابرین الان گوروتین بلاک شده آمادست که دوباره اجرا بشهامیدوارم که این پست تونسته باشه خلاصه ای از نحوی کار channel ها توی Golang رو شرح بده از این که زمان گذاشتید و این پست رو خوندید خیلی ممنون . خوشحال میشم اگه این پست رو خوندید در کامنت ها نظرات خودتون رو مطرح کنید.منابع:Diving Deep Into The Golang ChannelsHow Does Golang Channel WorksGetting Started With Golang Channels