Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use <-chan and chan<- for one directional communication?

Tags:

go

channel

I'm working on understanding Go's channels. I think I understand a basic bidirectional chan but I'm falling short at understanding <-chan and chan<-.

I expected them to be useful for communicating one way to a thread but I'm having issues with the thread actually reading and receiving the value.

package main

import (
    "fmt"
    "time"
)

func Thread(c chan<- int) {
    for {
        num := <-c
        fmt.Println("Thread : ", num)
        time.Sleep(time.Second)
    }
}

func main() {
    c := make(chan<- int, 3)
    go Thread(c)
    for i := 1; i <= 10; i++ {
        c <- i
    }
    for len(c) > 0 {
        time.Sleep(100)
    }
}

I've tried using <-chan instead of chan<- in the make() but the same kind of thing happens:

C:\>go run chan.go
# command-line-arguments
.\chan.go:10: invalid operation: <-c (receive from send-only type chan<- int)

If I can't read from the channel, why bother writing to it? With that thought in mind, I figure I must be doing something wrong. I had the expectation that a send only chan would mean that one thread can only send while the other thread can only receive. This does not seem to be the case.

If I remove the <- entirely, it works, but that would make it bidirectional allowing the go routine to respond (even though it never does) and I'm looking to avoid that. It seems like I can banish numbers to a chan that I'll never be able to read from, or that I could read from a chan that's impossible to write to.

What I'm hoping to do is send integers from the main thread to the go routine for it to print using a one way channel. What am I doing wrong?

This is with go 1.3.3 on Windows if it matters. Updating to 1.4 didn't help. I might want to mention this is all x64 as well.

like image 238
Corey Ogburn Avatar asked Mar 18 '23 12:03

Corey Ogburn


2 Answers

The Go Programming Language Specification

Channel types

A channel provides a mechanism for concurrently executing functions to communicate by sending and receiving values of a specified element type. The value of an uninitialized channel is nil.

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

The optional <- operator specifies the channel direction, send or receive. If no direction is given, the channel is bidirectional. A channel may be constrained only to send or only to receive by conversion or assignment.

You can be explicit about channel direction by conversion or assignment. For example, by conversion,

package main

import (
    "fmt"
    "time"
)

func Thread(r <-chan int) {
    for {
        num := <-r
        fmt.Println("Thread : ", num)
        time.Sleep(time.Second)
    }
}

func main() {
    c := make(chan int, 3)
    s, r := (chan<- int)(c), (<-chan int)(c)
    go Thread(r)
    for i := 1; i <= 10; i++ {
        s <- i
    }
    for len(c) > 0 {
        time.Sleep(100)
    }
}

Output:

Thread :  1
Thread :  2
Thread :  3
. . .

Or, equivalently, by assignment,

package main

import (
    "fmt"
    "time"
)

func Thread(r <-chan int) {
    for {
        num := <-r
        fmt.Println("Thread : ", num)
        time.Sleep(time.Second)
    }
}

func main() {
    c := make(chan int, 3)
    var s chan<- int = c
    var r <-chan int = c
    go Thread(r)
    for i := 1; i <= 10; i++ {
        s <- i
    }
    for len(c) > 0 {
        time.Sleep(100)
    }
}
like image 166
peterSO Avatar answered Mar 31 '23 15:03

peterSO


Go allows to create send- or receive-only channels like c := make(<-chan int), however, I haven't came across a use case. There is a discussion here in github.

About the error in the code;

func Thread(c chan<- int) {
    for {
        num := <-c
        fmt.Println("Thread : ", num)
        time.Sleep(time.Second)
    }
}

Function parameter c can only be sent to. Attempting to receive from c will result in a compiler error but the line num := <-c tries to receive.

At the end, fixed version;

package main

import (
    "fmt"
    "time"
)

func Thread(c <-chan int) {
    for {
        num := <-c
        fmt.Println("Thread : ", num)
        time.Sleep(time.Second)
    }
}

func main() {
    c := make(chan int, 3)
    go Thread(c)
    for i := 1; i <= 10; i++ {
        c <- i
    }
    for len(c) > 0 {
        time.Sleep(100)
    }
}
like image 43
Alper Avatar answered Mar 31 '23 14:03

Alper