گوروتین ها را می توان به عنوان یک thread سبک در نظر گرفت که بصورت مستقلانه می تواند همزمان با سایر گوروتین های دیگر کارها را انجام دهد. و همچنین گوروتین ها می توانند به واسطه کانال داده ها را بین هم اشتراک گذاری و منتقل کنند.
حداکثر اندازه stack یک گوروتین در زبان گو ۱ گیگابایت می باشد.
1var maxstacksize uintptr = 1 << 20 // enough until runtime.main sets it for real 2 3 if newsize > maxstacksize || newsize > maxstackceiling { 4 if maxstacksize < maxstackceiling { 5 print("runtime: goroutine stack exceeds ", maxstacksize, "-byte limit\n") 6 } else { 7 print("runtime: goroutine stack exceeds ", maxstackceiling, "-byte limit\n") 8 } 9 print("runtime: sp=", hex(sp), " stack=[", hex(gp.stack.lo), ", ", hex(gp.stack.hi), "]\n") 10 throw("stack overflow") 11 }
در زیر با استفاده از کلمه کلیدی go
یک نمونه گوروتین ایجاد کردیم و توجه کنید شما فقط توابع را می توانید همزمانی کنید.
1go functionName(parameters)
به عنوان مثال :
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func main() {
9 go start()
10 fmt.Println("Started")
11 time.Sleep(1 * time.Second)
12 fmt.Println("Finished")
13}
14
15func start() {
16 fmt.Println("In Goroutine")
17}
در کد فوق ما تابع ()start
را بواسطه کلمه کلیدی go
داخل گوروتین قرار دادیم و این تابع بصورت مستقلانه از تابع main اجرا شد. اما این وسط یک نکته ای وجود دارد. همانطور که گفتیم تابع اصلی جهت اجرا برنامه های زبان گو تابع main می باشد و اگر شما تابعی را بواسطه گوروتین از main جدا کنید ممکن است فرآیندهای داخل تابع main زود اتمام شود و شما خروجی تابعی که داخل گوروتین گذاشتید را نبینید.
ما در کد بالا با استفاده از تابع Sleep پکیج time یک وقفه ۱ ثانیه گذاشتیم و این وقفه باعث شد تا عملیات داخل تابع ()start تمام شود و خروجی نمایش داده شود.
خب حالا بزارید مثال فوق را بدون وقفه تست کنیم :
1package main
2import (
3 "fmt"
4)
5func main() {
6 go start()
7 fmt.Println("Started")
8 fmt.Println("Finished")
9}
10func start() {
11 fmt.Println("In Goroutine")
12}
در خروجی بالا هرگز پیغام داخل تابع ()start چاپ نمی شود.
1In Goroutine
علت اصلی این اتفاق این است که تابع main خودش داخل یک گوروتین اجرا می شود و زمانیکه شما یک تابع دیگری را داخل گوروتین قرار می دهید تا لحظه ای که تابع برای اجرا برنامه ریزی شود برنامه اتمام می شود.
3.2.1 گوروتین تابع main #
تابع main را وقتی می توانید ایجاد کنید که نام پکیج شما main و گوروتین اصلی شما main است. همه گوروتین ها از تابع main شروع می شوند و گوروتین ها بطور همزمان باز می توانند سایر گوروتین ها را اجرا کنند.
زمانیکه شما تابع main را فراخوانی می کنید بخش اصلی و شروع برنامه شما است. و اگر تابع main شما به هر دلیلی متوقف شود یا اتمام شود سایر گوروتین ها از بین می روند.
گوروتین ها چیزی به نام parent یا child ندارند. زمانیکه شما یک گوروتین را اجرا می کنید این گوروتین در کنار سایر گوروتین ها اجرا می شود و کارش را انجام می دهد. زمانی کار یک گوروتین تمام می شود که تابع بازگشت (return) داشته باشد.
بزارید یک مثال بزنیم تا ببینید چیزی به نام parent یا child برای گوروتین نداریم :
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func main() {
9 go start()
10 fmt.Println("Started")
11 time.Sleep(1 * time.Second)
12 fmt.Println("Finished")
13}
14
15func start() {
16 go start2()
17 fmt.Println("In Goroutine")
18}
19func start2() {
20 fmt.Println("In Goroutine2")
21}
در کد بالا داخل تابع main ما تابع start را با گوروتین اجرا کردیم و داخل تابع start تابع start2 را با گوروتین اجرا کردیم. این ۲ تابع start و start2 در کنار هم اجرا می شود و در نهایت کارشان اتمام می شود و هیچ کدام منتظر دیگری نخواهد بود.
3.2.2 ایجاد گوروتین چندتایی #
شما می توانید n تا گوروتین بطور همزمان در کنار هم اجرا کنید, در زیر یک مثال زدیم ببینید :
1package main
2
3import (
4 "fmt"
5 "time"
6)
7
8func execute(id int) {
9 fmt.Printf("id: %d\n", id)
10}
11
12func main() {
13 fmt.Println("Started")
14 for i := 0; i < 10; i++ {
15 go execute(i)
16 }
17 time.Sleep(time.Second * 2)
18 fmt.Println("Finished")
19}
1$ go run main.go
2Started
3id: 4
4id: 9
5id: 1
6id: 0
7id: 8
8id: 2
9id: 6
10id: 3
11id: 7
12id: 5
13Finished
در کد فوق ما یک حلقه قرار دادیم از i صفر تا ۱۰ که داخلش تابع execute را ۱۰ بار اجرا می کند. و هربار اجرا می شود خروجی های مختلفی دارد و ترتیبی درست نخواهید علت این اتفاق این است بطور همزمان اجرا می شوند در کنار هم و هرکدام از گوروتین ها زودتر کارش تمام شود خروجی را نمایش می دهد. به همین دلیل ترتیب درستی نخواهد داشت.
3.2.3 زمانبندی گوروتین ها #
زمانیکه یک برنامه گو اجرا می شود. go runtime رشته های (threads) سیستم عامل را راه اندازی می کند که معادل تعداد CPU های logical قابل استفاده برای فرآیند فعلی است. هر یک از logical CPU ها یک هسته مجازی دارد.
1virtual_cores = x*number_of_physical_cores
در بالا x هست تعداد threads به ازای هر هسته CPU
در گو ما یک تابع به نام NumCPU داخل پکیج runtime داریم که می توانید تعداد logical Proccessors موجود برای برنامه گو را ببینید.
روی سیستم من عدد 8 را چاپ کرد یعنی سیستم من ۴ هسته که هر هسته دارای ۲ threads است. که قابل استفاده برای برنامه گو روی سیستم من می باشد.