في لغة البرمجة جو (Go)، تُعتبر ميزة التساير (Concurrency) أحد الجوانب البارزة التي تميز اللغة وتجعلها قوية في التعامل مع العمليات المتزامنة والتواصل بينها. يعتمد تصميم جو على مفهوم التساير عبر القنوات (Concurrency via Channels)، وهو يوفر آلية فعّالة لتنفيذ عدة دوال بشكل موازي. دعنا نستكشف كيف يمكن تحقيق ذلك بشكل مفصل.
في جو، يُستخدم المصطلح “goroutine” للإشارة إلى التنفيذ الموازي لدوال معينة. يمكنك إنشاء goroutines بسهولة باستخدام كلمة الرئيسية “go” متبوعة بالدالة التي ترغب في تنفيذها. على سبيل المثال:
gopackage main
import (
"fmt"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int) {
for job := range jobs {
fmt.Println("Worker", id, "started job", job)
time.Sleep(time.Second) // تمثيل عمل طويل الأمد
fmt.Println("Worker", id, "finished job", job)
results <- job * 2
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
// إنشاء الأعمال
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// إطلاق الأعمال بواسطة الـ goroutines
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// جمع النتائج
for a := 1; a <= numJobs; a++ {
<-results
}
}
في هذا المثال، يتم إنشاء قناتين، قناة jobs
لإرسال الأعمال إلى الـ goroutines وقناة results
لاستقبال النتائج منها. يتم إطلاق الـ goroutines باستخدام دالة worker
التي تستقبل الوظائف من jobs
وترسل النتائج إلى results
.
تجنب النسيان في الكود أن نقوم بإغلاق قناة jobs
بعد إرسال جميع الأعمال، وذلك لإعلام الـ goroutines بأنه لا يوجد المزيد من الأعمال المتوقعة.
يُظهر هذا المثال كيف يمكنك تحقيق تساير العمليات بسهولة في جو، وذلك من خلال إنشاء وإطلاق goroutines لتنفيذ الوظائف بشكل متزامن.
المزيد من المعلومات
بالطبع، دعونا نعمق أكثر في ميزة التساير في لغة جو ونسلط الضوء على بعض الجوانب الهامة:
-
القنوات (Channels): في المثال السابق، استخدمنا قنوات لتسهيل تبادل البيانات بين الـ goroutines. قناة
jobs
استُخدمت لإرسال الأعمال إلى الـ goroutines، في حين استُخدمت قناةresults
لاستقبال النتائج منها. القنوات تُستخدم لتحقيق التواصل الآمن والفعّال بين الـ goroutines. -
التحكم في التنفيذ (Select): جو يوفر تعليمة
select
التي تسمح لك باختيار العملية التي تحدث أولاً بين عدة قنوات. هذا يفيد في التحكم في تسلسل التنفيذ وتنظيم التواصل بين الـ goroutines. -
تنظيم الانتظار (Wait Groups): في البرامج المتزامنة، يمكن أن يكون من الضروري الانتظار حتى يكتمل تنفيذ جميع الـ goroutines قبل استكمال البرنامج الرئيسي. تُستخدم مجموعات الانتظار (Wait Groups) في جو لتحقيق هذا الهدف.
gopackage main
import (
"fmt"
"sync"
"time"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done() // تنبيه المجموعة بانتهاء الـ goroutine
for job := range jobs {
fmt.Println("Worker", id, "started job", job)
time.Sleep(time.Second)
fmt.Println("Worker", id, "finished job", job)
results <- job * 2
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
// إنشاء الأعمال
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// إطلاق الأعمال بواسطة الـ goroutines
for w := 1; w <= 3; w++ {
wg.Add(1) // إشارة ببدء goroutine جديدة
go worker(w, jobs, results, &wg)
}
// إغلاق القناة results عند انتهاء كل الـ goroutines
go func() {
wg.Wait() // انتظار انتهاء جميع الـ goroutines
close(results)
}()
// جمع النتائج
for result := range results {
fmt.Println("Result:", result)
}
}
هذا المثال يستخدم sync.WaitGroup
لضمان انتظار انتهاء جميع الـ goroutines قبل إغلاق قناة النتائج واستكمال البرنامج الرئيسي.
باستخدام هذه الميزات، يمكنك تنظيم تنفيذ البرامج التي تستفيد من فعّالية التساير في جو والتي تتيح لك الاستفادة القصوى من المتعدد المهام.