Bidirectional Channels
By default, channels in Go are bidirectional, which means they can be used to both send and receive values. This is the simplest form of a channel and is what you get when you create a channel without any direction-specific notation.
Creating a Bidirectional Channel#
Creating a bidirectional channel is straightforward:
example.go
string">"comment">// Create a bidirectional channel for string valuesmessages := make(chan string)
Using a Bidirectional Channel#
Let's look at a simple example that demonstrates sending and receiving on a bidirectional channel:
example.go
package mainimport string">"fmt"func main() {messages := make(chan string)string">"comment">// Send a value into the channel in a separate goroutinego func() {messages <- string">"ping"}()string">"comment">// Receive the value from the channelmsg := <-messagesfmt.Println(msg)}
In this example:
- We create a bidirectional channel called
messages - We launch a goroutine that sends the string "ping" into the channel
- The main goroutine receives the value from the channel
- The received value is printed
Benefits and Limitations#
Benefits:
- Simple to use and understand
- Flexible - can be used for both sending and receiving
- No need to specify direction when creating the channel
Limitations:
- Anyone can read and write to the channel
- This can cause problems in concurrent environments where you want to restrict operations
- No type safety to prevent incorrect usage
When to Use Bidirectional Channels#
Bidirectional channels are most appropriate when:
- You have a simple communication pattern
- The same goroutine might need to both send and receive
- You're prototyping and don't need strict access control
Common Patterns with Bidirectional Channels#
Ping-Pong Pattern#
example.go
package mainimport (string">"fmt"string">"time")func ping(ch chan string) {ch <- string">"ping"}func pong(ch chan string) {msg := <-chfmt.Println(msg)ch <- string">"pong"}func main() {ch := make(chan string)go ping(ch)go pong(ch)fmt.Println(<-ch)time.Sleep(100 * time.Millisecond) string">"comment">// Ensure the goroutines have time to execute}
Basic Worker Pattern#
example.go
package mainimport string">"fmt"func worker(jobs chan int, results chan int) {for job := range jobs {results <- job * 2 string">"comment">// Double the job value}}func main() {jobs := make(chan int, 5)results := make(chan int, 5)string">"comment">// Start a workergo worker(jobs, results)string">"comment">// Send jobsfor i := 1; i <= 3; i++ {jobs <- i}close(jobs)string">"comment">// Collect resultsfor i := 1; i <= 3; i++ {fmt.Println(<-results)}}
Summary#
- Bidirectional channels can be used for both sending and receiving
- They're created with
make(chan Type) - They're the default channel type in Go
- They offer flexibility but less type safety than directional channels
- They're great for simple communication patterns
In the next lab, we'll look at directional channels, which add type safety by restricting operations to either sending or receiving.