🍀 Channels কী?
Goroutine গুলো স্বাধীনভাবে চলে, তাই তাদের মধ্যে তথ্য আদান-প্রদান বা সিঙ্ক্রোনাইজেশনের (Sync) জন্য দরকার হয় একটি safe communication mechanism।
Channel হচ্ছে সেই নিরাপদ পাইপলাইন, যেখানে:
- এক Goroutine তথ্য পাঠায় (
chan <-) - অন্য Goroutine তথ্য গ্রহণ করে (
<- chan)
channel দিয়ে গোরুটিনগুলো একে অপরের সাথে “কথা বলে”।
Go বলে:
“Don’t communicate by sharing memory; share memory by communicating.”
মানে: একসাথে memory-তে read/write না করে, ডেটা channel দিয়ে পাস করো — তাতে লক (mutex) না লাগলেও concurrency safe থাকে।
কেন চ্যানেল দরকার?
ধরো, দুইটা goroutine একই variable-এ write করছে:
x = x + 1যদি দুজন একসাথে লেখে, তখন value corrupt হতে পারে (race condition)।
এটি এভাবে fix করা যায়:
- Mutex lock
- Atomic operations
- Shared memory coordination
কিন্তু Go বলে:
“Don’t communicate by sharing memory; share memory by communicating.”
মানে: Variable share করো না। Data পাঠাও। তাহলেই code naturally safe থাকে।
এজন্যই channel ব্যবহার করা হয়।
1) Channel তৈরি
ch := make(chan int) // unbuffered
ch2 := make(chan string, 3) // buffered, capacity 3এখানে ch শুধু int send/receive করতে পারবে।
Channel internal model:
Sender → [ Channel ] → ReceiverChannel নিজেই data queue ধরে রাখে (buffer থাকলে queue বড় হয়)।
2) Sending ও Receiving বিস্তারিত
Send:
ch <- xReceive:
v := <-chUnbuffered channel-এ:
Sender send করতে গিয়ে অপেক্ষা করে ⏳
Receiver receive করতে গিয়ে অপেক্ষা করে ⏳যেখানে দুজন মিলবে, সেখানেই data transfer হবে এবং দুজনই release হবে।
Unbuffered Timing Diagram
Time →
Sender: ----[ Send(n) ]------ wait ----> paired ----> done
Receiver: -------- wait --------[ Receive ]------------> doneঅর্থ:
Sender ও Receiver একই সময় data exchange করে। এটা handshake-like synchronization।
2) Send & Receive (Unbuffered Channel)
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
ch <- 10 // send
}()
value := <-ch // receive
fmt.Println(value)
}এখানে কী হচ্ছে?
- Sender
ch <- 10পর্যন্ত এসে receiver না আসা পর্যন্ত অপেক্ষা করবে। - Receiver
<-chপর্যন্ত এসে sender না পাঠানো পর্যন্ত অপেক্ষা করবে।
➡️ Unbuffered channel sender এবং receiver দুইজনকেই অপেক্ষায় রাখে = synchronization
3) Buffered Channel
ch := make(chan int, 2)এখানে channel-এর capacity = 2।
Buffer = Queue = Channel-এর ভেতরে memory।
package main
import "fmt"
func main() {
ch := make(chan int, 2)
ch <- 1
ch <- 2 // OK, buffer full
// ch <- 3 // এখানে deadlock, কারণ buffer পূর্ণ → receiver নেই
fmt.Println(<-ch)
fmt.Println(<-ch)
}Buffered Timing Diagram
Initial: [ empty ]
Send → [ 1 ]
Send → [ 1, 2 ] (full now)
Next Send → block! (receiver needed here)Receiver:
Receive → pops 1 → [ 2 ]
Receive → pops 2 → [ empty ]পার্থক্য ( Timing ধারণা )
| চ্যানেল | আচরণ | কবে send block হয়? | কবে receive block হয়? |
|---|---|---|---|
| Unbuffered | Send ও Receive দুজনই একে অপরের জন্য অপেক্ষা করে | যতক্ষণ receiver ready না হয় | যতক্ষণ sender কিছু না পাঠায় |
| Buffered | যতক্ষণ buffer-এ জায়গা আছে, sender অপেক্ষা করে না | Buffer পূর্ণ হলে | Buffer খালি হলে |
4) Channel বন্ধ করা (close)
close(ch)Close করা মানে:
- আর send করা যাবে না (send করলে panic)
- কিন্তু receive করা যাবে (zero-value + ok flag)
বন্ধ করা কেন দরকার?
♦️ বোঝাতে যে আর ডেটা আসবে না।
Receive with ok flag:
v, ok := <-ch
if !ok {
fmt.Println("Channel closed")
}Channel-এর ওপর loop করা (for-range):
for v := range ch {
fmt.Println(v)
}range channel close হওয়া পর্যন্ত চলবে।
Channel-এর আকার দেখা
len(ch) // buffer এ কয়টা item আছে
cap(ch) // buffer capacityউপকারী debugging এ।
সাধারণ ভুলগুলো (সবচেয়ে গুরুত্বপূর্ণ অংশ)
| ভুল | ফলাফল | কেন |
|---|---|---|
| Receive আছে কিন্তু Send নেই | Deadlock | কেউ data পাঠাচ্ছে না |
| Send আছে কিন্তু Receive নেই | Deadlock | channel overflow (unbuffered) |
| Buffered full, but no receiver | Deadlock | buffer পূর্ণ হয়ে গেছে |
| Close করার পর send | Panic | বন্ধ channel-এ লেখা যায় না |
| Close না করে range করলে | Infinite wait | কখনো close ধরবে না |
✅ অনুশীলন ১: Unbuffered Channel Synchronization Proof
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan string)
go func() {
fmt.Println("Sender: preparing")
time.Sleep(2 * time.Second)
fmt.Println("Sender: sending")
ch <- "Hello"
fmt.Println("Sender: done send")
}()
time.Sleep(1 * time.Second)
fmt.Println("Receiver: waiting...")
msg := <-ch
fmt.Println("Receiver got:", msg)
}Output reasoning:
Sender: preparing
Receiver: waiting...
Sender: sending
Receiver got: Hello
Sender: done sendদেখো send এবং receive handshake এর মত synchronized।
✅ অনুশীলন ২: Buffered এ রূপান্তর ও পার্থক্য
package main
import "fmt"
func main() {
ch := make(chan int, 1)
ch <- 10 // এখনই send হয়ে যায়, অপেক্ষা লাগে না
fmt.Println("Sent!")
fmt.Println(<-ch)
}Unbuffered হলে Sent! দেখাতে receiver লাগতো 🔥।
Buffered হলে sender block হয় না (buffer full না হওয়া পর্যন্ত)।
✅ অনুশীলন ৩: Channel বন্ধ করা + range
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
for i := 1; i <= 5; i++ {
ch <- i
}
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
}✅ অনুশীলন ৪: 10 সংখ্যা পাঠাও এবং প্রিন্ট করো
package main
import "fmt"
func main() {
ch := make(chan int)
go func() {
for i := 1; i <= 10; i++ {
ch <- i
}
close(ch)
}()
for v := range ch {
fmt.Println(v)
}
}✅ অনুশীলন ৫: Pipeline (Generator → Doubler → Main)
package main
import "fmt"
func generate(ch chan int) {
for i := 1; i <= 5; i++ {
ch <- i
}
close(ch)
}
func double(in chan int) chan int {
out := make(chan int)
go func() {
for v := range in {
out <- v * 2
}
close(out)
}()
return out
}
func main() {
ch := make(chan int)
go generate(ch)
out := double(ch)
for v := range out {
fmt.Println(v)
}
}🧩 চ্যালেঞ্জ প্রশ্ন (কেন deadlock হবে/হবে না?)
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1
fmt.Println(<-ch)
}📝 Channel Rules Summary (5 Points)
- Channels goroutine গুলোর মধ্যে safe communication নিশ্চিত করে।
- Unbuffered channel send-receive synchronize করে (দুজন অপেক্ষা করে)।
- Buffered channel buffer capacity পর্যন্ত non-blocking send করতে দেয়।
- Channel বন্ধ (
close(ch)) মানে আর ডেটা আসবে না; receiverrangeদিয়ে পড়তে পারে। - Deadlock = send/receive আছে কিন্তু বিপরীত পক্ষ নেই।