المستوى: متوسط — يُفترض إنك كاتبت go func() قبل كده وعارف Goroutine بتعمل إيه، لكن لسّه بتلخبط بين mutex و channel.
لو شغّلت 1000 Goroutine بيكتبوا في slice واحد بدون تنسيق، Go runtime مش بيرفع لك خطأ. الـ binary بيشتغل، يطلع نتيجة "تبدو سليمة"، وبعد 4 ساعات في الإنتاج بـ panic فجأة على "concurrent map writes". Channels بتقفل الباب ده بسطر واحد بدل 14 سطر mutex، ولو طبّقتها صح هتنزّل عدد bugs الـ concurrency في الكود لـ صفر.
Channels في Go: قناة الاتصال الآمنة بين الـ Goroutines
المشكلة باختصار
الـ Goroutine زي الموظف اللي بيشتغل لوحده. ممتاز لما الشغل مستقل. بس لو 8 موظفين عايزين يكتبوا في نفس الجدول وقت واحد، حد لازم ينظمهم. Go بيقدّم اختيارين: sync.Mutex (قفل تقليدي) أو chan (قناة اتصال). الفرق العملي: mutex بيخلي اتنين يتشاركوا متغير. Channel بيخلي اتنين يتكلموا بدون ما يتشاركوا أي حاجة. الفلسفة دي مكتوبة بالحرف في توثيق Go الرسمي على لسان Rob Pike: "Don't communicate by sharing memory; share memory by communicating."
إيه هي Channel فعلاً؟ مثال خط إنتاج المعمل (للمبتدئين)
تخيّل معمل تعبئة عصير. عندك 5 عمال على خط الفرز (Producers) و 3 على خط التغليف (Consumers). بدل ما الفارز يجري للمغلِّف عشان يسلّمه التفاحة في إيده — وده هيخلق فوضى لو الفارز بيشتغل أسرع من المغلِّف — فيه سير ناقل في النص. الفارز يحط على السير، المغلِّف ياخد. لو السير ملا، الفارز يستنّى تلقائيًا. لو السير فاضي، المغلِّف يستنّى. ولا حد محتاج يحط قفل على حد.
الـ Channel هو السير الناقل بالظبط. بنفس الفكرة بالظبط.
التعريف العلمي الدقيق: Channel في Go هي FIFO queue typed بحجم محدد، بتوفّر عمليتي send و receive بشكل atomic ومُتزامن. الفكرة الرياضية ورا التصميم مأخوذة من ورقة Communicating Sequential Processes (CSP) للعالم Tony Hoare سنة 1978، اللي اتطبقت في لغة occam الأول، وبعدها Go اعتمدتها كنموذج concurrency أساسي. كل send/receive في حد ذاته synchronization point — يعني ما تحتاجش mutex خارجي عشان تحمي البيانات اللي بتمر.
Unbuffered vs Buffered: الفرق اللي بيكسر الـ deadlock
// Unbuffered: send بيستنى receive (handshake)
ch := make(chan int)
go func() { ch <- 42 }() // الـ goroutine بتقف لحد ما حد ياخد
val := <-ch // val == 42
// Buffered: send مش بيستنى لحد ما الـ buffer يملا (mailbox)
ch := make(chan int, 100)
ch <- 1 // ما بيستناش
ch <- 2 // ما بيستناش
// لو وصلت لـ 100، السطر الـ 101 هيقف لحد ما حد ياخد