خلاصه مختصر مفید GoLang (پارت سوم)

این مقاله پارت سوم از آموزش مختصر مفید GoLang هست. میتونین پارت اول رو اینجا و پارت دوم رو اینجا ببینید.پارت سوم آموزش مختصر مفید GoLangتوی این سری آموزش (خلاصه مختصر مفید GoLang) قرار نبود و نیست که کاملاً وارد جزئیات بشیم به همین خاطر به نحوه نصب و راه اندازی و یسری نکات دیگه اشاره ای نشده و سعی میکنم فقط با مفاهیم مهم بصورت خلاصه آشنا بشیم.توی پارت اول با مفاهیم کاملا پایه ای (مثل تعریف متغیر و توابع و ...) آشنا شدیم و توی پارت دوم اشاره گر رو خوندیم و یکم بیشتر با تعریف توابع و استفاده ازشون آشنا شدیم.توی این پارت میخوایم برسیم به دو تا مبحثی که میشه یجورایی گفت اصلی ترین ویژگی های GoLang هستن. گو روتین ها و کانال هاگو روتین هابرای اینکه مفهوم گو روتین ها رو درک کنیم باید با نخ و چند نخی آشنا باشیم. دوستانی که با مفهوم thread و و multi thread آشنا نیستن، ما بعضی وقتا نیاز داریم تسک هامون بصورت Concurrency یا همزمان انجام بشن. در حقیقت وقتی یه پروسس رو اجرا میکنیم، میتونه چندین thread یا نخ داشته باشه. توی حالت عادی برنامه فقط یه thread داره و ما میتونیم برناممون رو با چند thread اجرا کنیم. و مفهومش اینه که یسری تسک ها رو موازی با همدیگه پیش ببریم. جدای از اینکه سخت افزاری که کد قراره روش اجرا بشه باید این قضیه رو ساپورت کنه (که اکثراً همیشه میکنن سخت افزارای امروزی) باید زبونی که باهاش کد میزنیم هم این روند رو ساپورت کنه (که بازم اکثر زبونایی که سری تو سرا در اوردن به شکل خیلی خوبی پشتیبانی میکنن).گو هم به خوبی از پس این داستان بر میاد. برای این بتونیم بعضی تسک ها رو موازی با بقیه تکسها اجرا کنیم، به راحتی بصورت زیر میتونیم این کار رو بکنیم:اوپس.. همین؟! بله! به همین راحتیتنها کاری که لازم داریم بکنیم اینه که زمان صدا زدن یه تابع، کلمه ی کلیدی go رو قبلش بنویسیم. با این کار GoLang یه به اصطلاح routine ایجاد کرده و تابعمون رو جداگانه و موازی با روند عادی برنامه، توی اون روتین اجرا میکنه. هر برنامه ی گو یه main routine داره و میتونه یعالمه اصطلاحا child routine هم داشته باشه که همه بصورت همزمان و موازی باهم تسک هایی رو انجام بدن که با اتمام کار main routine بقیه child routine ها هم میرن تو دیوار:حالا میرسیم به جای جالبترِ داستان. بعضی زبونای برنامه نویسی اجازه میدن تسک هایی رو موازی با تسک دیگه اجرا کنی، ولی معمولا یا دسترسی مستقیمی بهش نداری، یا مدیریتشون سخته، یا اینکه اون نخ ها بصورت پیشفرض با حالت استانداردی نمیتونن مستقیم پیغامی برای همدیگه بفرستن.حالا زبون گو ویژگی ای داره که این مسائل رو هم حل کرده، اگه گفتین چی؟ کانالها!کانال هاکانال ها ویژگی های باحالی دارن، مثل اینکه میتونیم به کمکشون منتظر بمونیم تا یه روتین یا چند تا روتین کارشون رو بکنن و به ما جواب بدن، یا باحالتر از اون اینه که کانالها میتونن پیام هایی با type دلخواه رو بین روتین ها جابجا کنن.برای یه مثال ساده کد زیر رو ببینیم:package main import &quotfmt&quot func main() { c := make(chan string) go func() { c <- &quotHello&quot }() fmt.Println(<-c) }توی کد بالا دستورc := make(chan string)به زبون گو میگیم که یه chan یا همون کانال از نوع string و به اسم c ایجاد کن (یعنی کانالی ایجاد کن که بین روتین ها بتونه string جابجا کنه).func() { c <- &quotHello&quot }()این تیکه از کد یه تابعه و داخلش داره (با اون یه خط کد و ساختاری که میبینین) نوشته ی Hello رو میفرسته روی کانالی که ساختیم (و به کمک کلمه کلیدی go این کار رو داره جداگانه و بصورت همزمان روی یه روتین جدید انجام میده). و خط زیرfmt.Println(<-c)وقتی به c-> میرسه اگه پیغامی روی کانال c باشه برمیداره، اگه نباشه منتظر میمونه تا پیغامی روی c گذاشته بشه، و اون رو پاس میده به تابع fmt.Println برای نمایش به کاربر.خب پس بصورت خلاصه یه کانال ایجاد کردیم، با ایجاد یه روتین جدید تابعی رو روی اون روتین ران کردیم و توی main routine برناممون اون رو گرفتیم و نشون دادیم.میخوایم یه مثال دیگه ببینیم که به کمک اون بتونیم ببینیم که یسری لینک (مثلا چندتا سایت) رو میتونیم ببینیم یا نه (میخوایم یه درخواست http get بدیم ببینیم میتونیم جواب بگیریم یا نه)links := []string{ &quothttp://google.com&quot, &quothttps://virgool.io&quot, &quothttps://www.youtube.com&quot, }خب، اول از همه باید چیکار کنیم؟ یه کانال میسازیم:c := make(chan string)یه تابع هم میسازیم که میخوایم توش چک کنیم ببینیم یه لینک به ما جواب میده یا نه:func checkLink(link string, c chan string) { _, err := http.Get(link) if err == nil { fmt.Println(link, &quot founded&quot) c <- link } else { fmt.Println(link, &quot not found!&quot) c <- link } }برای استفاده از http.Get باید "net/http" رو به پروژه import کنیم که اگه از ویژوال استودیو کد (VSCode) استفاده کنید خودش اتومات با سیو کردن کدتون، import های لازمه رو انجام میده. با http.Get یه درخواست Get به لینکی که میخوایم میزنیم، و قسمت زیر: _, errبرای اینه که این تابع دو تا چیز بر میگردونه، اولی جوابیه که از Get گرفته و دومی یه ارور. برای ما جوابی که میاد مهم نیست پس اولی رو _ گذاشتیم، برای ما فقط این مهم که ببینیم خطایی توی این درخواست رخ داده یا نه، اگه err برابر nil باشه یعنی تونستیم جواب بگیریم در غیر این صورت خطایی رخ داده و معنیش عدم دریافت جوابیه که ما میخواستیم.و به کمک خط زیر تک تک لینک ها رو با go میفرستیم به این تابع:for _, link := range links { go checkLink(link, c) }و خیلی راحت با اجرا کردن این برنامه، متوجه میشیم که هیچ اتفاقی نمیفته!! یعنی چی؟ مفهومش اینه که ما به ازای هر لینک یه روتین ایجاد کردیم و اون روتین ها میخوان جوابی روی کانال بذارن، اما قبل از اینکه کار به گذاشتن پیامِ اونا برسه کار main routine تموم شده و اون child routine ها هم تمومه کارشون.برای اینکه هم منتظر جواب روتین ها بمونیم، هم برنامه ای نوشته باشیم که همینجوری هی ارتباط رو تا بینهایت تست کنه، کد زیر رو اضافه میکنیم:for l := range c { go func() { time.Sleep(5 * time.Second) checkLink(l, c) }() }نکته ای که اتفاق میفته دو تا چیزه، یکی خروجیه که به این شکله:یکی دیگه اینکه توی بعضی نرم افزارا بصورت زیریه خطی چیزی یه خطایی رو مشخص میکنه که مثلا توی VSCode خطای زیر رو میگه بهمون:خودمونیش اینکه داری اشتبا میزنی، بذارین اینجوری کدمون رو تصحیح کنیم:for l := range c { go func(link string) { time.Sleep(5 * time.Second) checkLink(link, c) }(l) }که خروجی میشه این:این شد چیزی که ما میخوایم. حالا دلیلش چیه؟ اینکه توی حالت اول، وقتی از توی for l := range c مستقیم l رو پاس میدیم به تابع checkLink، عملا زبون گو زمانی که داره روی c حلقه for رو اعمال میکنه، هر کدوم رو میریزه توی یه خونه از حافظه، و وقتی l رو پاس دادیم توی تابع checkLink از یجایی به بعد تکراری اون خونه از حافظرو ارسال میکنه. اما وقتی که حالت دوم رو پیاده کردیم، داریم به ازای هر بار فراخوانی تابع func، مقدار خودش رو دوباره پاس میدیم بهش و این لینک که درست هم هست به تابع checklink پاس داده میشه. اگه یکم پیچیدس حق دارین گیج بشین ولی یکم با تمرین و کدزنی متوجه میشین قضیه چیه. کد کامل این مثال اینجوری میشه:package main import ( &quotfmt&quot &quotnet/http&quot &quottime&quot ) func main() { links := []string{ &quothttp://google.com&quot, &quothttps://virgool.io&quot, &quothttps://www.youtube.com&quot, } c := make(chan string) for _, link := range links { go checkLink(link, c) } for l := range c { go func(link string) { time.Sleep(5 * time.Second) checkLink(link, c) }(l) } } func checkLink(link string, c chan string) { _, err := http.Get(link) if err == nil { fmt.Println(link, &quot founded&quot) c <- link } else { fmt.Println(link, &quot not found!&quot) c <- link } }به کمک دستور select هم میتونین چندتا کانال ایجاد کنید و منتظر بمونید تا خروجی هاشون رو دریافت کنین و ... چیز به درد بخوریه و ساختار پیاده سازیش شبیه switch هست. در موردش بخونید حتمامنتشر شده در ویرگول توسط محمد قدسیان https://virgool.io/@mohammad.ghodsianhttps://virgool.io/@mohammad.ghodsian/%D8%AE%D9%84%D8%A7%D8%B5%D9%87-%D9%85%D8%AE%D8%AA%D8%B5%D8%B1-%D9%85%D9%81%DB%8C%D8%AF-golang-%D9%BE%D8%A7%D8%B1%D8%AA-%D8%B3%D9%88%D9%85-lqwhva3jm3qh