Channels in Go

List Topics
February 18, 2025
No Comments
6 min read

In Go, channels are a way for goroutines to communicate with each other. They provide a mechanism to send and receive data between goroutines in a safe and synchronized manner. Channels help avoid race conditions by allowing goroutines to pass data directly.

Here’s a detailed explanation of channels with examples.

1️⃣ Declaring and Using Channels

You can create a channel using the make function. Channels have a type, which determines the type of data they can carry.

🔹 Example:

Go
package main

import "fmt"

func main() {
    // Create a channel of type string
    ch := make(chan string)

    // Start a goroutine to send data to the channel
    go func() {
        ch <- "Hello from the channel!"
    }()

    // Receive the data from the channel
    msg := <-ch
    fmt.Println(msg)
}

🔹 Output:

Bash
Hello from the channel!

👉 In this example, a goroutine sends the string "Hello from the channel!" into the channel ch, and the main function receives it.

2️⃣ Buffered Channels

A buffered channel allows you to send multiple values without a corresponding receiver immediately being ready. The size of the buffer determines how many values can be stored in the channel.

🔹 Example:

Go
package main

import "fmt"

func main() {
    // Create a buffered channel with a capacity of 2
    ch := make(chan string, 2)

    // Sending values into the buffered channel
    ch <- "Message 1"
    ch <- "Message 2"

    // Receiving values from the channel
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

🔹 Output:

Bash
Message 1
Message 2

👉 Here, the buffered channel ch can store two messages before requiring a receiver to read them.

3️⃣ Channel Direction

You can specify the direction of data flow in a channel—either send-only or receive-only. This helps enforce correct usage in function parameters and return types.

🔹 Example:

Go
package main

import "fmt"

// Function that only sends data to a channel
func sendMessage(ch chan<- string, msg string) {
    ch <- msg
}

// Function that only receives data from a channel
func receiveMessage(ch <-chan string) {
    fmt.Println(<-ch)
}

func main() {
    ch := make(chan string)

    go sendMessage(ch, "Hello, Channel Direction!")
    receiveMessage(ch)
}

🔹 Output:

Bash
Hello, Channel Direction!

👉 The sendMessage function is restricted to sending data, while receiveMessage is restricted to receiving data from the channel.

4️⃣ Closing a Channel

When you’re done sending data into a channel, you can close it using the close function. Once closed, no more data can be sent to the channel.

🔹 Example:

Go
package main

import "fmt"

func main() {
    ch := make(chan int)

    go func() {
        for i := 1; i <= 5; i++ {
            ch <- i
        }
        close(ch)  // Close the channel
    }()

    // Range over the channel until it's closed
    for val := range ch {
        fmt.Println(val)
    }
}

🔹 Output:

Bash
1
2
3
4
5

👉 After sending 5 values, the channel is closed. The for loop iterates over the channel until it's closed.

5️⃣ Select Statement

The select statement is used to handle multiple channels concurrently. It waits for one of the multiple channel operations to proceed.

🔹 Example:

Go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "Message from ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Message from ch2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

🔹 Output:

Bash
Message from ch1
Message from ch2

👉 The select statement waits for messages from either ch1 or ch2. Whichever channel sends data first is handled.

6️⃣ Channel and Goroutine Synchronization

Channels are often used to synchronize goroutines, ensuring that one goroutine waits for another to complete.

🔹 Example:

Go
package main

import "fmt"

func worker(done chan bool) {
    fmt.Println("Working...")
    done <- true  // Signal that the work is done
}

func main() {
    done := make(chan bool)
    go worker(done)

    <-done  // Wait for the worker to finish
    fmt.Println("Worker done!")
}

🔹 Output:

Bash
Working...
Worker done!

👉 Here, the main function waits for the worker goroutine to finish by receiving a value from the done channel.

Summary:

  • Channels in Go are used to send and receive data between goroutines safely.
  • Channels can be unbuffered (synchronous) or buffered (asynchronous).
  • Channels can be send-only or receive-only for better control.
  • You can close a channel to indicate no more data will be sent.
  • The select statement allows you to work with multiple channels concurrently.

Channels are a key part of Go's concurrency model, allowing efficient and safe communication between goroutines.


চ্যানেল (Channels) গুলোর ব্যবহার

Go প্রোগ্রামিং ভাষায় Channels একটি উপায় যেখানে goroutine-এর মধ্যে ডেটা আদান-প্রদান করা যায়। Channels ব্যবহার করে দুটি goroutine নিরাপদ ও সিঙ্ক্রোনাইজড উপায়ে ডেটা পাঠাতে ও গ্রহণ করতে পারে। Channels এর মাধ্যমে race conditions এড়ানো যায় কারণ এটি সরাসরি ডেটা পাস করতে সাহায্য করে।

এখানে Channels নিয়ে বিস্তারিত ব্যাখ্যা এবং কিছু উদাহরণ দেওয়া হলো।

1️⃣ Channel ঘোষণা ও ব্যবহার

Channel তৈরি করতে make ফাংশন ব্যবহার করা হয়। Channel-এর একটি টাইপ থাকে, যা নির্ধারণ করে Channel-এর মধ্যে কোন ধরনের ডেটা পাঠানো যাবে।

🔹 উদাহরণ:

Go
package main

import "fmt"

func main() {
    // একটি string টাইপ Channel তৈরি করা হলো
    ch := make(chan string)

    // একটি goroutine শুরু করা হলো, যেটি Channel-এ ডেটা পাঠাবে
    go func() {
        ch <- "Hello from the channel!"
    }()

    // Channel থেকে ডেটা গ্রহণ করা হলো
    msg := <-ch
    fmt.Println(msg)
}

🔹 আউটপুট:

Bash
Hello from the channel!

👉 এই উদাহরণে, একটি goroutine Channel-এ "Hello from the channel!" পাঠাচ্ছে, এবং main ফাংশন সেই ডেটা গ্রহণ করছে।

2️⃣ Buffered Channels

একটি Buffered Channel আপনাকে একাধিক মান পাঠাতে দেয়, যখন কোনো রিসিভার তা গ্রহণের জন্য সরাসরি প্রস্তুত থাকে না। Buffer-এর সাইজ নির্ধারণ করে কতগুলো মান Channel-এ জমা হতে পারবে।

🔹 উদাহরণ:

Go
package main

import "fmt"

func main() {
    // একটি buffered channel তৈরি করা হলো যার capacity ২
    ch := make(chan string, 2)

    // Channel-এ মান পাঠানো হচ্ছে
    ch <- "Message 1"
    ch <- "Message 2"

    // Channel থেকে মান গ্রহণ করা হচ্ছে
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

🔹 আউটপুট:

Bash
Message 1
Message 2

👉 এই উদাহরণে, buffered channel ch ২টি মেসেজ ধরে রাখতে পারে, রিসিভার ডেটা পড়ার আগেই।

3️⃣ Channel Direction

Channel-এর ডেটা প্রবাহের দিক নির্দেশ করা যায়— হয় send-only (শুধু পাঠানো যাবে) অথবা receive-only (শুধু গ্রহণ করা যাবে)। এটি ফাংশনের প্যারামিটার ও রিটার্ন টাইপে সঠিক ব্যবহার নিশ্চিত করতে সাহায্য করে।

🔹 উদাহরণ:

Go
package main

import "fmt"

// শুধুমাত্র ডেটা পাঠানোর ফাংশন
func sendMessage(ch chan<- string, msg string) {
    ch <- msg
}

// শুধুমাত্র ডেটা গ্রহণ করার ফাংশন
func receiveMessage(ch <-chan string) {
    fmt.Println(<-ch)
}

func main() {
    ch := make(chan string)

    go sendMessage(ch, "Hello, Channel Direction!")
    receiveMessage(ch)
}

🔹 আউটপুট:

Bash
Hello, Channel Direction!

👉 sendMessage ফাংশন শুধুমাত্র ডেটা পাঠানোর জন্য, আর receiveMessage ফাংশন শুধুমাত্র ডেটা গ্রহণ করার জন্য ব্যবহৃত হচ্ছে।

4️⃣ Channel বন্ধ করা

ডেটা পাঠানো শেষ হলে, close ফাংশন ব্যবহার করে Channel বন্ধ করা যায়। Channel বন্ধ হলে, আর কোনো ডেটা পাঠানো সম্ভব নয়।

🔹 উদাহরণ:

Go
package main

import "fmt"

func main() {
    ch := make(chan int)

    go func() {
        for i := 1; i <= 5; i++ {
            ch <- i
        }
        close(ch)  // Channel বন্ধ করা হলো
    }()

    // Channel এর ওপর `range` করে ডেটা পড়া হচ্ছে যতক্ষণ না এটি বন্ধ হয়
    for val := range ch {
        fmt.Println(val)
    }
}

🔹 আউটপুট:

Bash
1
2
3
4
5

👉 ৫টি মান পাঠানোর পরে, Channel বন্ধ করা হয়। for লুপ Channel এর উপর iterate করে যতক্ষণ তা বন্ধ না হয়।

5️⃣ Select Statement

select স্টেটমেন্টটি একাধিক Channel-এর সাথে একযোগে কাজ করার জন্য ব্যবহৃত হয়। এটি একাধিক Channel অপারেশন থেকে যেকোনো একটির জন্য অপেক্ষা করে।

🔹 উদাহরণ:

Go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)

    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "Message from ch1"
    }()

    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Message from ch2"
    }()

    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
}

🔹 আউটপুট:

Bash
Message from ch1
Message from ch2

👉 select স্টেটমেন্টটি অপেক্ষা করে ch1 অথবা ch2 থেকে ডেটা আসার জন্য এবং যেটি আগে আসে সেটিকে প্রসেস করে।

6️⃣ Channel দিয়ে Synchronization করা

Channels প্রায়ই goroutine-এর মধ্যে Synchronization করার জন্য ব্যবহৃত হয়, যার মাধ্যমে একটি goroutine আরেকটি goroutine-এর কাজ শেষ হওয়ার জন্য অপেক্ষা করতে পারে।

🔹 উদাহরণ:

Go
package main

import "fmt"

func worker(done chan bool) {
    fmt.Println("Working...")
    done <- true  // সিগন্যাল দিচ্ছে যে কাজ শেষ
}

func main() {
    done := make(chan bool)
    go worker(done)

    <-done  // worker goroutine-এর কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করছে
    fmt.Println("Worker done!")
}

🔹 আউটপুট:

Bash
Working...
Worker done!

👉 এখানে main ফাংশন worker goroutine-এর কাজ শেষ হওয়া পর্যন্ত অপেক্ষা করে, done Channel থেকে ডেটা গ্রহণ করার মাধ্যমে।

সারসংক্ষেপ:

  • Channels ব্যবহার করে goroutine-এর মধ্যে ডেটা পাঠানো ও গ্রহণ করা যায়।
  • Channels হতে পারে unbuffered (সিঙ্ক্রোনাইজড) অথবা buffered (অ্যাসিঙ্ক্রোনাইজড)।
  • Channels-এর ডেটা প্রবাহকে send-only অথবা receive-only করা যায়।
  • Channels বন্ধ করা যায় close ফাংশন দিয়ে, এবং পরে আর কোনো ডেটা পাঠানো যাবে না।
  • select স্টেটমেন্ট একাধিক Channel-এর মধ্যে একযোগে কাজ করতে দেয়।

Channels Go-এর concurrency মডেলের একটি গুরুত্বপূর্ণ অংশ, যা goroutine-এর মধ্যে নিরাপদ এবং কার্যকরী যোগাযোগ নিশ্চিত করে।

©2025 Linux Bangla | Developed & Maintaind by Linux Bangla.