Golang Pro: الشبكات وتعدد مؤشرات الترابط وهياكل البيانات والتعلم الآلي مع Go

صورة مرحبا سكان!



هل أنت بالفعل على دراية بأساسيات لغة Go؟ و هذا الكتاب من اجلك. سيوضح Michalis Tsukalos قدرات اللغة ، ويقدم تفسيرات واضحة وبسيطة ، ويعطي أمثلة ويقترح أنماط برمجة فعالة. بينما تستكشف الفروق الدقيقة في Go ، ستتقن أنواع بيانات اللغة وهياكلها ، بالإضافة إلى التغليف والتزامن وبرمجة الشبكة وتصميم المترجم والتحسين والمزيد. ستساعد المواد والتمارين في نهاية كل فصل على تعزيز معرفتك الجديدة. ستكون المادة الفريدة هي الفصل الخاص بالتعلم الآلي في Go ، والتي ستوجهك من التقنيات الإحصائية الأساسية إلى الانحدار والتكتل. سوف تتعلم التصنيف والشبكات العصبية وتقنيات الكشف عن الشذوذ. في الأقسام التطبيقية ، ستتعلم كيفية استخدام Go with Docker و Kubernetes و Git و WebAssembly و JSON والمزيد.



عن ماذا هذا الكتاب
1 «Go » Go , godoc , Go-. , . , Go .



2 «Go » Go . unsafe, , Go- C, C- — Go.



, defer, strace(1) dtrace(1). , Go, Go WebAssembly.



3 « Go» , Go: , -, , , , . !



4 « » Go struct, , , , . , switch, strings math/big, Go « — » XML JSON.



5 « Go » , Go . , , -, , . Go container, , Go .



6 « Go» , init(), Go- syscall, text/template html/template. , , go/scanner, go/parser go/token. Go!



7 « » Go: , . , - Go Go- Delve.



8 « UNIX-, » Go. , flag , UNIX, , bytes, io.Reader io.Writer, Viper Cobra Go. : Go, Go Systems Programming!



9 « Go: , » , — , Go.



, , , sync Go.



10 « Go: » . , ! Go, select, Go, , , sync.Mutex sync.RWMutex. context, , (race conditions).



11 «, » , , - , , Go-, Go-.



12 « Go» net/http , - - Go. http.Response, http.Request http.Transport, http.NewServeMux. , Go -! , Go DNS-, Go gRPC.



13 « : » HTTPS- Go UDP TCP net. , RPC, Go TCP- «» .



14 « Go» Go , , , , , TensorFlow, Go Apache Kafka.



. Go, , Go-, Go-, Go C WebAssembly, Go. 5 6 7. Go- , Go- Go.



Go. 8–11 Go, Go, , . Go.

Go WebAssembly, Docker Go, Viper Cobra, JSON YAML, , , go/scanner go/token, git(1) GitHub, atomic, Go gRPC HTTPS.



, Go-, . : -, , , -, .



ومرة أخرى عن Go-channels



بمجرد استخدام الكلمة الأساسية المحددة ، هناك عدة طرق فريدة لاستخدام أنابيب Go التي تقوم بأكثر مما رأيته في الفصل 9. في هذا القسم ، ستتعرف على الطرق المختلفة التي تستخدم بها أنابيب Go.



اسمحوا لي أن أذكرك أن القيمة الصفرية للقنوات لا شيء ، وإذا أرسلت رسالة إلى قناة مغلقة ، فسينتقل البرنامج إلى وضع الذعر. ولكن إذا حاولت قراءة البيانات من قناة مغلقة ، فستحصل على قيمة صفرية لنوع القناة هذا. وبالتالي ، بعد إغلاق القناة ، لم يعد بإمكانك كتابة البيانات إليها ، ولكن لا يزال بإمكانك قراءتها.



لإغلاق القناة ، ليس من الضروري تصميمها لتلقي البيانات فقط. بالإضافة إلى ذلك ، يتم حظر القناة الصفرية دائمًا ، أي أن محاولة القراءة أو الكتابة من القناة صفر ستحظر القناة. تعد خاصية القنوات هذه مفيدة للغاية عندما تريد تعطيل فرع من جملة select عن طريق ضبط متغير القناة على لا شيء.



أخيرًا ، عند محاولة إغلاق القناة الصفرية ، سيثير البرنامج حالة من الذعر. لنلقِ نظرة على مثال closeNilChannel.go:



package main

func main() {
      var c chan string
      close(c)
}


سيؤدي تنفيذ closeNilChannel.go إلى النتيجة التالية:



$ go run closeNilChannel.go
panic: close of nil channel
goroutine 1 [running]:
main.main()
       /Users/mtsouk/closeNilChannel.go:5 +0x2a
exit status 2


قنوات الإشارة



قناة الإشارات هي قناة تُستخدم فقط للإشارة. ببساطة ، يمكن استخدام قناة الإشارة عندما تريد إبلاغ برنامج آخر بشيء ما. لا يلزم استخدام قنوات التشوير لنقل البيانات.



لا ينبغي الخلط بين قنوات التشوير ومعالجة إشارة UNIX التي تمت مناقشتها في الفصل 8 ، فهي أشياء مختلفة تمامًا.


سنناقش لاحقًا في هذا الفصل مثال على الكود الذي يستخدم قنوات التشوير.



القنوات المخزنة



موضوع هذا القسم الفرعي هو أنابيب مخزنة. هذه هي القنوات التي تسمح لجدول Go جدولة المهام بسرعة للتعامل مع المزيد من الطلبات. بالإضافة إلى ذلك ، يمكن استخدامها كإشارات للحد من عرض النطاق الترددي للتطبيق.



تعمل الطريقة المعروضة هنا على النحو التالي: يتم إعادة توجيه جميع الطلبات الواردة إلى قناة تقوم بمعالجتها بدورها. عندما تنتهي القناة من معالجة الطلب ، فإنها ترسل رسالة إلى المتصل الأصلي مفادها أن القناة جاهزة لمعالجة طلب جديد. وبالتالي ، فإن سعة المخزن المؤقت للقناة تحدد عدد الطلبات المتزامنة التي يمكن للقناة تخزينها.



سننظر في هذه الطريقة باستخدام كود برنامج bufChannel.go كمثال. دعونا نقسمها إلى أربعة أجزاء.



يبدو الجزء الأول من كود bufChannel.go كما يلي:



package main

import (
       "fmt"
)


يحتوي الجزء الثاني من ملف bufChannel.go على كود Go التالي:



func main() {
      numbers := make(chan int, 5)
      counter := 10


يسمح لك تعريف الأرقام المقدم هنا بتخزين ما يصل إلى خمسة أعداد صحيحة في هذا الأنبوب.



يحتوي الجزء الثالث من bufChannel.go على كود Go التالي:



for i := 0; i < counter; i++ {
     select {
     case numbers <- i:
     default:
            fmt.Println("Not enough space for", i)
     }
}


في هذا الرمز ، حاولنا وضع عشرة أرقام في قناة الأرقام. ومع ذلك ، نظرًا لأن الأعداد بها مساحة لخمسة أعداد صحيحة فقط ، فلا يمكننا تخزين الأعداد العشرة كلها فيها.



يبدو باقي كود Go من bufChannel.go على النحو التالي:



   for i := 0; i < counter+5; i++ {
        select {
              case num := <-numbers:
                    fmt.Println(num)
              default:
                    fmt.Println("Nothing more to be done!")
              break
        }
   }
}


في كود Go هذا ، حاولنا قراءة محتويات قناة الأرقام باستخدام حلقة for وبيان select. طالما أن هناك شيئًا يمكن قراءته في قناة الأرقام ، فسيتم تنفيذ الفرع الأول من جملة التحديد. عندما تكون قناة الأرقام فارغة ، يتم تنفيذ الفرع الافتراضي.



سيؤدي تنفيذ bufChannel.go إلى النتيجة التالية:



$ go run bufChannel.go
Not enough space for 5
Not enough space for 6
Not enough space for 7
Not enough space for 8
Not enough space for 9
0
1
2
3
4
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!
Nothing more to be done!


قنوات صفرية



في هذا القسم ، ستتعرف على القناة صفر. هذا نوع خاص من القنوات يتم حظره دائمًا. سننظر إلى هذه القنوات باستخدام برنامج nilChannel.go كمثال. دعونا نقسمها إلى أربعة أجزاء من التعليمات البرمجية.



يبدو الجزء الأول من nilChannel.go كما يلي:



package main

import (
       "fmt"
       "math/rand"
       "time"
)


يحتوي الجزء الثاني من nilChannel.go على كود Go التالي:



func add(c chan int) {
      sum := 0
      t := time.NewTimer(time.Second)

      for {
           select {
           case input := <-c:
                 sum = sum + input
           case <-t.C:
                 c = nil
                 fmt.Println(sum)
           }
      }
}


هنا ، باستخدام الوظيفة add () كمثال ، يوضح كيفية استخدام قناة الصفر. يحجب عامل التشغيل <-tC القناة C من المؤقت t للوقت المحدد في استدعاء الوقت. NewTimer (). لا تخلط بين القناة c ، وهي وسيطة دالة ، مع القناة tC ، التي تنتمي إلى جهاز ضبط الوقت t. عندما ينتهي الوقت ، يرسل المؤقت قيمة إلى قناة tC ، والتي تبدأ في تنفيذ الفرع المقابل من عبارة select - يقوم بتعيين القناة c على صفر ويعرض قيمة متغير المجموع.



يبدو مقتطف الشفرة الثالث nilChannel.go كما يلي:



func send(c chan int) {
      for {
           c <- rand.Intn(10)
      }
}


الغرض من وظيفة الإرسال () هو إنشاء أرقام عشوائية وإرسالها إلى القناة طالما أن القناة مفتوحة.



يبدو باقي كود Go في nilChannel.go كما يلي:



func main() {
      c := make(chan int)
      go add(c)
      go send(c)
      time.Sleep(3 * time.Second)
}


وظيفة time.Sleep () مطلوبة بحيث يكون لدى الجوروتين وقت كافٍ للتنفيذ.



سيؤدي تشغيل nilChannel.go إلى النتائج التالية:



$ go run nilChannel.go
13167523
$ go run nilChannel.go
12988362


نظرًا لأن عدد مرات تنفيذ الفرع الأول من جملة select في add () غير ثابت ، فإن تشغيل nilChannel.go عدة مرات سيؤدي إلى نتائج مختلفة.



قنوات القنوات



قناة القناة هي نوع خاص من متغيرات القناة التي تعمل مع قنوات أخرى بدلاً من أنواع المتغيرات المعتادة. ومع ذلك ، ما زلت بحاجة إلى تعريف نوع بيانات لقناة من القنوات. لتحديد قناة القنوات ، استخدم كلمة chan الأساسية مرتين على التوالي ، كما هو موضح في البيان التالي:



c1 := make(chan chan int)


الأنواع الأخرى من القنوات المعروضة في هذا الفصل أكثر شيوعًا وفائدة من قنوات القنوات.


سنستعرض استخدام قنوات القنوات باستخدام مثال الكود الموجود في chSquare.go. دعونا نقسمها إلى أربعة أجزاء.



يبدو الجزء الأول من chSquare.go كما يلي:



package main

import (
       "fmt"
       "os"
       "strconv"
       "time"
)

var times int


يحتوي الجزء الثاني من chSquare.go على كود Go التالي:



func f1(cc chan chan int, f chan bool) {
      c := make(chan int)
      cc <- c
      defer close(c)

      sum := 0
      select {
      case x := <-c:
            for i := 0; i <= x; i++ {
                 sum = sum + i
            }
            c <- sum
      case <-f:
            return
      }
}


بعد الإعلان عن قناة عادية من النوع int ، نقوم بتمريرها إلى متغير القناة. بعد ذلك ، باستخدام عبارة select ، نحصل على فرصة لقراءة البيانات من قناة int العادية أو الخروج من الوظيفة باستخدام قناة الإشارة f.



بعد قراءة قيمة واحدة من القناة c ، نقوم بتشغيل حلقة for تحسب مجموع كل الأعداد الصحيحة من 0 إلى القيمة الصحيحة التي قرأناها للتو. ثم نرسل القيمة المحسوبة إلى قناة int c ، وهذا كل شيء.



يحتوي الجزء الثالث من chSquare.go على كود Go التالي:



func main() {
      arguments := os.Args
      if len(arguments) != 2 {
          fmt.Println("Need just one integer argument!")
          return
      }
      times, err := strconv.Atoi(arguments[1])
      if err != nil {
           fmt.Println(err)
           return
      }

      cc := make(chan chan int)


في السطر الأخير من مقتطف الشفرة هذا ، نعلن عن متغير قناة يسمى cc. هذا المتغير هو نجم هذا البرنامج ، لأن كل شيء يعتمد عليه. يتم تمرير المتغير cc إلى f1 () ويستخدم في حلقة for التالية.



يبدو باقي كود chSquare.go Go كما يلي:



   for i := 1; i < times+1; i++ {
        f := make(chan bool)
        go f1(cc, f)
        ch := <-cc
        ch <- i
        for sum := range ch {
             fmt.Print("Sum(", i, ")=", sum)
        }
        fmt.Println()
        time.Sleep(time.Second)
        close(f)
    }
}


القناة f هي قناة الإشارة لنهاية goroutine عندما يتم الانتهاء من كل العمل. تسمح لك التعليمات ch: = <-cc بالحصول على قناة عادية من متغير قناة لتمرير قيمة int هناك باستخدام عامل ch <- i. بعد ذلك ، نقرأ البيانات من الأنبوب باستخدام حلقة for. تمت برمجة الدالة f1 () لإرجاع قيمة واحدة ، ولكن يمكننا أيضًا قراءة قيم متعددة. لاحظ أن كل قيمة i يتم تقديمها بواسطة goroutine الخاص بها.



يمكن أن يكون نوع قناة الإشارة أي شيء ، بما في ذلك المنطقية المستخدمة في الكود السابق والبنية {} ، والتي سيتم استخدامها لقناة الإشارة في القسم التالي. الميزة الرئيسية لقناة إشارة من النوع Struct {} هي أنه لا يمكن إرسال بيانات إلى مثل هذه القناة ، وهذا يمنع حدوث الأخطاء.



سيؤدي تنفيذ chSquare.go إلى نتائج مثل هذه:



$ go run chSquare.go 4
Sum(1)=1
Sum(2)=3
Sum(3)=6
Sum(4)=10
$ go run chSquare.go 7
Sum(1)=1
Sum(2)=3
Sum(3)=6
Sum(4)=10
Sum(5)=15
Sum(6)=21
Sum(7)=28


اختيار تسلسل تنفيذ goroutines



لست مطالبًا بعمل أي افتراضات حول تسلسل تنفيذ goroutines. ومع ذلك ، هناك أوقات يكون من الضروري فيها التحكم في هذا الطلب. في هذا القسم الفرعي ، ستتعلم كيفية القيام بذلك باستخدام قنوات الإشارة.



قد تسأل ، "لماذا إنشاء goroutines ثم تنفيذها بترتيب معين عندما يكون من الأسهل فعل الشيء نفسه مع الوظائف العادية؟" الجواب بسيط: يمكن أن تعمل goroutines بشكل متزامن وتنتظر اكتمال goroutines الأخرى ، في حين لا يمكن للوظائف لأنها تنفذ بالتتابع.


في هذا القسم الفرعي ، سنلقي نظرة على برنامج Go يسمى تعريف_مرر. دعونا نقسمها إلى خمسة أجزاء. يبدو الجزء الأول من selectOrder.go كما يلي:



package main

import (
       "fmt"
       "time"
)

func A(a, b chan struct{}) {
      <-a
      fmt.Println("A()!")
      time.Sleep(time.Second)
      close(b)
}


الوظيفة A () مقفلة بواسطة القناة المخزنة في المعلمة a. بمجرد فتح هذه القناة في main () ، ستبدأ الوظيفة A () في العمل. أخيرًا ، سيتم إغلاق القناة b ، وبالتالي إلغاء حظر وظيفة أخرى - في هذه الحالة ، B ().



يحتوي الجزء الثاني من selectOrder.go على كود Go التالي:



func B(a, b chan struct{}) {
      <-a
      fmt.Println("B()!")
      close(b)
}


منطق الوظيفة B () هو نفس منطق A (). هذه الوظيفة محظورة حتى يتم إغلاق القناة a. ثم تقوم بعملها وتغلق القناة ب. لاحظ أن القناتين a و b تشيران إلى أسماء معلمات الوظيفة.



يبدو الجزء الثالث من التعليمات البرمجية لـ selectOrder.go كما يلي:



func C(a chan struct{}) {
      <-a
      fmt.Println("C()!")
}


وظيفة C () محظورة وتنتظر إغلاق القناة a قبل البدء.



يحتوي الجزء الرابع من selectOrder.go على الكود التالي:



func main() {
      x := make(chan struct{})
      y := make(chan struct{})
      z := make(chan struct{})


ستصبح هذه القنوات الثلاث معلمات لثلاث وظائف.



يحتوي آخر مقتطف من selectOrder.go على كود Go التالي:



     go C(z)
     go A(x, y)
     go C(z)
     go B(y, z)
     go C(z)

     close(x)
     time.Sleep(3 * time.Second)
}


هنا يقوم البرنامج بجميع الوظائف الضرورية ، ثم يغلق القناة x وينام لمدة ثلاث ثوان.



سينتج عن تنفيذ selectOrder.go النتيجة المرجوة ، على الرغم من أنه سيتم استدعاء الوظيفة C () عدة مرات:



$ go run defineOrder.go
A()!
B()!
C()!
C()!
C()!


استدعاء C () عدة مرات كجروتين لن يسبب مشاكل ، لأن C () لا يغلق أي قنوات. ولكن إذا اتصلت بـ A () أو B () أكثر من مرة ، فمن المرجح أن تظهر رسالة خطأ ، على سبيل المثال:



$ go run defineOrder.go
A()!
A()!
B()!
C()!
C()!
C()!
panic: close of closed channel
goroutine 7 [running]:
main.A(0xc420072060, 0xc4200720c0)
       /Users/mtsouk/Desktop/defineOrder.go:12 +0x9d
created by main.main
       /Users/mtsouk/Desktop/defineOrder.go:33 +0xfa
exit status 2


كما ترى ، هنا تم استدعاء الوظيفة A () مرتين. ومع ذلك ، عندما يغلق A () قناة ، يكتشف أحد goroutines أن القناة مغلقة بالفعل ويخلق حالة من الذعر عندما يحاول إغلاق تلك القناة مرة أخرى. إذا حاولنا استدعاء الدالة B () أكثر من مرة ، فسنحصل على حالة ذعر مماثلة.



كيف لا تستخدم goroutines



في هذا القسم ، ستتعلم طريقة ساذجة لفرز الأعداد الطبيعية باستخدام goroutines. البرنامج الذي سننظر فيه يسمى sillySort.go. دعونا نقسمها إلى قسمين. يبدو الجزء الأول من sillySort.go كما يلي:



package main

import (
       "fmt"
       "os"
       "strconv"
       "sync"
       "time"
)

func main() {
      arguments := os.Args

      if len(arguments) == 1 {
          fmt.Println(os.Args[0], "n1, n2, [n]")
          return
      }

      var wg sync.WaitGroup
      for _, arg := range arguments[1:] {
           n, err := strconv.Atoi(arg)
           if err != nil || n < 0 {
                fmt.Print(". ")
                continue
           }


يحتوي الجزء الثاني من sillySort.go على كود Go التالي:



           wg.Add(1)
           go func(n int) {
                defer wg.Done()
                time.Sleep(time.Duration(n) * time.Second)
                fmt.Print(n, " ")
           }(n)
      }

      wg.Wait()
      fmt.Println()
}


يتم إجراء الفرز عن طريق استدعاء الوقت. وظيفة النوم () - كلما زاد الرقم الطبيعي ، كلما استغرق الأمر وقتًا أطول قبل تنفيذ عامل التشغيل fmt.Print ()!



سيؤدي تنفيذ sillySort.go إلى نتائج مثل هذه:



$ go run sillySort.go a -1 1 2 3 5 0 100 20 60
. . 0 1 2 3 5 20 60 100
$ go run sillySort.go a -1 1 2 3 5 0 100 -1 a 20 hello 60
. . . . . 0 1 2 3 5 20 60 100
$ go run sillySort.go 0 0 10 2 30 3 4 30
0 0 2 3 4 10 30 30




نبذة عن الكاتب



ميهاليس تسوكالوس هو مسؤول UNIX ومبرمج ومسؤول قاعدة بيانات وعالم رياضيات. يحب كتابة الكتب والمقالات التقنية ، تعلم شيء جديد. بالإضافة إلى هذا الكتاب ، كتب Michalis Go Systems Programming بالإضافة إلى أكثر من 250 مقالة فنية للعديد من المجلات ، بما في ذلك Sys Admin و MacTech و Linux User and Developer و Usenix ؛ تسجيل الدخول: Linux Format و Linux Journal. الاهتمامات البحثية لـ Michalis هي قواعد البيانات والتصور والإحصاءات والتعلم الآلي.



حول المحرر العلمي



يعمل Mat Ryer على كتابة برامج الكمبيوتر منذ سن السادسة: أولاً في BASIC لـ ZX Spectrum ، ثم مع والده ، على AmigaBASIC و AMOS لـ Commodore Amiga. قضى الكثير من الوقت في نسخ التعليمات البرمجية يدويًا من سجل تنسيق Amiga ، وتغيير قيم المتغيرات أو مراجع بيان GOTO لمعرفة ما جاء منها. دفعت نفس روح الاستكشاف والهوس بالبرمجة مات البالغ من العمر 18 عامًا للعمل في منظمة محلية في مانسفيلد بالمملكة المتحدة ، حيث بدأ في بناء مواقع الويب والخدمات الأخرى عبر الإنترنت.



بعد عدة سنوات من العمل باستخدام تقنيات مختلفة في مجالات مختلفة ، ليس فقط في لندن ولكن في جميع أنحاء العالم ، حول مات انتباهه إلى لغة برمجة أنظمة جديدة تسمى Go ، تم استخدامها لأول مرة في Google. نظرًا لأن Go كان يحل المشكلات الفنية الساخنة والساخنة جدًا ، بدأ Mat في استخدام اللغة لحل المشكلات عندما كان Go لا يزال في مرحلة تجريبية واستمر في البرمجة فيه منذ ذلك الحين. عملت Mat في العديد من المشاريع مفتوحة المصدر ، وأنشأت العديد من حزم Go ، بما في ذلك Testify و Moq و Silk and Is ومجموعة أدوات مطور MacOS BitBar



منذ عام 2018 ، كان مات مؤسسًا مشاركًا لـ Machine Box ، لكنه لا يزال يشارك في المؤتمرات ويكتب عن Go على مدونته ، وهو عضو نشط في مجتمع Go.



»يمكن العثور على مزيد من التفاصيل حول الكتاب على موقع الناشر

» جدول المحتويات

» مقتطفات للساكنين



خصم 25٪ على القسيمة - Golang



عند الدفع مقابل النسخة الورقية من الكتاب ، يتم إرسال كتاب إلكتروني إلى البريد الإلكتروني.



All Articles