Directional Channel in Golang

Learn via video courses
Topics Covered

Overview

Channels are a fundamental tool for achieving concurrency in Go, allowing goroutines to communicate and synchronize with one another. However, channels can be created in two different ways: unidirectional or bidirectional. In this article, we will learn about the directions in the channel in a detail.

Why are Directions Provided to Channels in Golang?

In Go, channels are used to provide a way for goroutines (lightweight threads) to communicate with one another and synchronize their execution. Channels are used to send and receive values between goroutines. The direction of a channel is specified using the keyword chan followed by the type of the values that will be sent over the channel. The direction can be either chan for sending and receiving or chan <- for sending only or <- chan for receiving only. This allows for more precise control over the communication between goroutines and can help prevent bugs caused by incorrectly using a channel for sending or receiving.

  • chan: bidirectional channel (Both read and write)
  • chan <- : only writing to channel
  • <- chan: only reading from the channel (input channel)

Types of Directional Channels in Golang

Unidirectional

In Go, a unidirectional channel is a channel that can only be used for sending or receiving, but not both. This is specified by using the "<-" operator to indicate the direction of the channel.

Unidirectional channels are useful in situations where you want to ensure that a channel is only used in the intended way. For example, if you have a function that should only receive values from a channel, you can use a unidirectional channel to ensure that the function cannot accidentally send values on the channel.

Example of Unidirectional Channel:

Output:

Run the code!

Explanation of the above code:

  • In the above example, we created separate channels for receiving and sending values using a Unidirectional Channel.
  • The first line creates a receive-only channel of type <-chan string using the make function. This channel can only be used to receive values of type string.
  • The second line creates a send-only channel of type chan<- string using the make function. This channel can only be used to send values of type string.
  • In the main function, the variable ch1 is assigned to the receive-only channel, and ch2 is assigned to the send only channel.
  • Then, the fmt. Printf function is used to display the type of each channel on the screen. The first %T format specifier is used to display the type of ch1, and the second %T format specifier is used to display the type of ch2.

Receive Only Channels in Golang

Similarly, a unidirectional channel for receiving only can be created using the following syntax:

  • The <- symbol after the chan keyword indicates that this channel can only be used for receiving values of type int.
  • This channel can be used to receive values of type int, but attempting to send to it will result in a compile-time error.

When trying to send data to such a channel will give the below error.

For Example:

Output:

Run the code!

Solving the above error,

For Example:

Output:

Run the code!

Explanation of the above code:

  • In this example, we have created a buffered channel of type chan int with a buffer size of 3 using the make function. Then it sends a value 2 to the channel.
  • In the main function, the variable ch is assigned to the buffered channel and then the value 2 is sent to the channel using the channel operator <-.
  • Then the function process is called, passing the channel as a parameter. In the process function, the channel is received using the channel operator <- and the received value is stored in the variable s. The received value is then printed to the console using the fmt.Println(s).

Send Only Channels in Golang

A unidirectional channel for sending only can be created using the following syntax:

  • The <- symbol before the chan keyword indicates that this channel can only be used for sending values of type int.
  • This channel can be used to send values of type int, but attempting to receive from it will result in a compile-time error.

For Example:

Output:

Run the code!

Explanation of the above code:

  • In this example, we have created a send-only channel c of type chan string with a buffer size of 1 using the make function. Then it calls a function f passing the channel as a parameter.
  • In the main function, the variable c is assigned to the buffered channel. The function f is called passing the channel as a parameter.
  • In the function f, the channel is passed as a parameter with the type chan<- string, meaning it is a send-only channel. It sends a string "send only channel" to the channel using the channel operator <-.
  • Back in the main function, the value that was sent to the channel is received using the channel operator <- and printed to the console using fmt.Println(<-c).

Bidirectional Channel

In Go, a Bidirectional channel is a channel that can be used for both sending and receiving values. It is declared using the keyword "chan" followed by the type of the values that will be sent over the channel. For example, a bidirectional channel of type string would be declared as a "chan string".

A bidirectional channel can be used to send and receive values from multiple goroutines. The sending and receiving of values are done using the channel operator "<-". When a value is sent over a bidirectional channel using the "<-" operator, it becomes available for reading by another goroutine. When a value is received from a bidirectional channel using the "<-" operator, it is removed from the channel.

Bidirectional channels are useful for synchronizing the execution of multiple goroutines and for passing data between them. They can be used to implement a wide range of concurrency patterns, such as pipelines, fan-in, fan-out, and more.

Syntax:

Once a channel is created, you can use the <- operator to send and receive values over the channel.

However, it is important to note that when sending or receiving from a channel, it will block the execution of the goroutine until a corresponding goroutine is available to receive or send.

For Example:

Output:

Run the code!

Explanation of the above code:

  • In this example, we create a bidirectional channel much using the make(chan int) function. Then, we launch a goroutine that sends the value 71 to the channel using the <- operator. In the main goroutine, we read the value from the channel using the same operator and print it out.
  • As you can see, the bidirectional channel can be used for both sending and receiving values between goroutines.

Problems with the Bidirectional Channel in Golang

Bidirectional Channels are good for both receive and send values, but they come with some major issues such as:

  • Race conditions: When several goroutines attempt to read from or write to the same channel at the same time, the order of execution is uncertain. This can lead to race conditions and unexpected behavior in the program.

  • Deadlocks: If a goroutine is trying to receive from a channel that no other goroutine is sending to, it will block indefinitely. Similarly, if a goroutine is trying to send to a channel that no other goroutine is trying to receive from, it will also block indefinitely. This can create a deadlock situation.

  • Unnecessary complexity: When bidirectional channels are used, the flow of data can become more complex and harder to reason about, especially in large programs with many goroutines.

Output:

Run the code!

Explanation of the code:

  • This is the most common declaration format seen when working with channels. However, any channel built in this manner will be bidirectional by default. This means that anyone with access to a channel value may both read and write to it.
  • This can cause issues in a concurrent environment, and many a Go programmer has pulled their hair out while debugging a panic: send on a closed channel.
  • There should be something where only the sender should close a channel,
  • Only the sender knows when there is no more data to broadcast, and it is the receiver's responsibility to keep an eye out for the shutdown, or, preferably, to just range over the channel, quitting the loop naturally when it's done. If this order is disrupted, it is usually a warning that something is seriously wrong, therefore it's panic.

Converting Bidirectional Channel into the Unidirectional Channel

In the Go programming language, you can convert a bidirectional channel into a unidirectional channel, or to put it another way, a bidirectional channel can be converted into a receive-only or send-only channel, but not the other way around. As demonstrated in the programme below:

Output :

Run the code!

Explanation of the code:

  • In the above code, we create a bidirectional channel in the main function.
  • Next, we are creating a sending() which will help to convert the bidirectional channel into send only channel.
  • Finally, we are sending it only inside goroutine because outside the goroutine it's bidirectional and prints scaler.

We can also create a separate channel, one for sending and one for receiving, and use them in place of the bidirectional channel.

Conclusion

  • In this article, channels are used to send and receive data between goroutines. There are two types of channels: unidirectional and bidirectional.
  • A unidirectional channel only allows data to flow in one direction, either for sending or for receiving. This means that a unidirectional channel can only be used for sending or receiving, but not for both. This can help to reduce the potential for bugs and make the flow of data in the program more clear and predictable.
  • On the other hand, a bidirectional channel allows data to flow in both directions, meaning that a bidirectional channel can be used for both sending and receiving. However, bidirectional channels can lead to confusion and complexity when trying to reason about the flow of data in a program.
  • It is generally recommended to use unidirectional channels in Go to make the flow of data in a program more clear and predictable. However, bidirectional channels can be useful in certain situations where data needs to flow in both directions.
  • Go channels are a powerful tool for communication between goroutines, but it is important to understand the difference between unidirectional and bidirectional channels and to choose the appropriate type for a given situation to reduce the potential for bugs and make the flow of data in a program more clear and predictable.