I have the following code:
package main
import "net"
import "fmt"
import "bufio"
func main() {
conn, _ := net.Dial("tcp", "irc.freenode.net:6667")
reader := bufio.NewReader(conn)
go func() {
str, err := reader.ReadString('\n')
if err != nil {
// handle it
fmt.Println(err)
}
fmt.Println(str)
}()
}
If I don't have the code that reads from the buffer in a goroutine, it outputs a message like this, which is what I expect to happen:
:zelazny.freenode.net NOTICE * :*** Looking up your hostname...
However, having it inside a goroutine prints nothing.
Can someone explain why that is?
Your program will exit when the main() function finishes. This is likely to happen before your goroutine has time to run and print its output.
One option would be to have the main goroutine block reading from a channel, and have the goroutine write to the channel when it has completed its work.
Another common way to "wait for a goroutines end", is using WaitGroup:
http://golang.org/pkg/sync/#WaitGroup . You can use waitGroup.Add(1) for each started goroutine, then use waitGroup.Done() in each goroutine after it finishes. In the main function you can use waitGroup.Wait() and this will wait until waitGroup.Done() has been called for each added goroutine.
Write data to a channel ch at the end of goroutine and read data from ch out of goroutine can make the main function waiting for goroutine print message.
Here is an example:
package main
import "net"
import "fmt"
import "bufio"
func main() {
conn, _ := net.Dial("tcp", "irc.freenode.net:6667")
reader := bufio.NewReader(conn)
ch := make(chan byte, 1)
go func() {
str, err := reader.ReadString('\n')
if err != nil {
// handle it
fmt.Println(err)
}
fmt.Println(str)
ch <- 1
}()
<-ch
}
You need to add a time delay like time.Sleep(3 * time.Second) (this waits 3 seconds).
The goroutine closes when the program terminates. I had the exact same problem.
Related
I found some example with "Catch values from Goroutines"-> link
There is show how to fetch value but if I want to return value from several goroutines, it wont work.So, does anybody know, how to do it?
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"sync"
)
// WaitGroup is used to wait for the program to finish goroutines.
var wg sync.WaitGroup
func responseSize(url string, nums chan int) {
// Schedule the call to WaitGroup's Done to tell goroutine is completed.
defer wg.Done()
response, err := http.Get(url)
if err != nil {
log.Fatal(err)
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
// Send value to the unbuffered channel
nums <- len(body)
}
func main() {
nums := make(chan int) // Declare a unbuffered channel
wg.Add(1)
go responseSize("https://www.golangprograms.com", nums)
go responseSize("https://gobyexample.com/worker-pools", nums)
go responseSize("https://stackoverflow.com/questions/ask", nums)
fmt.Println(<-nums) // Read the value from unbuffered channel
wg.Wait()
close(nums) // Closes the channel
// >> loading forever
Also, this example, worker pools
Is it possible to get value from result: fmt.Println(<-results) <- will be error.
Yes, just read from the channel multiple times:
answerOne := <-nums
answerTwo := <-nums
answerThree := <-nums
Channels function like thread-safe queues, allowing you to queue up values and read them out one by one
P.S. You should either add 3 to the wait group or not have one at all. The <-nums will block until a value is available on nums so it is not necessary
The following piece of code try to send to the channel on the main goroutine and receive from another goroutine but a few times it returns as expected but a few times it exits without printing any on the console screen
package main
import "fmt"
func main() {
ch := make(chan bool)
go func() {
data := <-ch
fmt.Printf("Received: %t", data)
}()
ch <- true
}
At the same time, the following piece of code works as expected everytime, one difference is that an additional check has been added to check if the channel is closed or not which always throws the same expected output.
Does this ensure that a check on the channel is a must than optional ? or anything wrong with the code
package main
import "fmt"
func main() {
ch := make(chan bool)
go func() {
data, ok := <-ch
if !ok {
fmt.Println("Channel closed")
return
}
fmt.Printf("Received: %t", data)
}()
ch <- true
}
You should wait for goroutine to complete before main routine exit.
package main
import (
"fmt"
"sync"
)
func main() {
ch := make(chan bool)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
data := <-ch
fmt.Printf("Received: %t", data)
}()
ch <- true
wg.Wait()
}
The thing is your second piece of code doesn't print Received: true every time. I tested it several times.
As #jub0bs mentioned there is no guarantee that your goroutine finishes before the main routine. You must control it yourself.
The sample of code:
package main
import (
"io"
"os"
"os/signal"
"sync"
"syscall"
)
func main() {
sigintCh := make(chan os.Signal, 1)
signal.Notify(sigintCh, syscall.SIGINT, syscall.SIGTERM)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
io.Copy(os.Stdout, os.Stdin)
}()
<-sigintCh
os.Stdin.Close()
wg.Wait()
}
If run this sample and try to interrupt by ^C it waits for any input and stops only after sending something to stdin (e.g. just press enter).
I expect that closing Stdin will be like sending EOF, but it doesn't work.
Closing os.Stdin will cause io.Copy to return with error file already closed next time it reads from it (after CTRL-C, try pressing Enter).
As explained in the File.Close docs:
Close closes the File, rendering it unusable for I/O.
You cannot force an EOF return from os.Stdin by closing it (or any other way). Instead, you would need to either wrap os.Stdin and implement your own Read method that conditionally returns EOF, or read a limited number of bytes in a loop.
You can see some more discussion and possible workarounds on this golang-nuts thread.
You can interrupt an io.Copy without closing the source side - by passing an io.Reader that has been wrapped with logic that takes a cancelable context.Context outlined here.
Modify your above goroutine like so:
ctx, cancel := context.WithCancel(context.Background())
go func() {
defer wg.Done()
r := NewReader(ctx, os.Stdin) // wrap io.Reader to make it context-aware
_, err := io.Copy(os.Stdout, r)
if err != nil {
// context.Canceled error if interrupted
}
}()
<-sigintCh
cancel() // canceling context will interrupt io.Copy operation
You can import NewReader from an external package like github.com/jbenet/go-context/io or inline a snippet from the blog link above:
type readerCtx struct {
ctx context.Context
r io.Reader
}
func (r *readerCtx) Read(p []byte) (n int, err error) {
if err := r.ctx.Err(); err != nil {
return 0, err
}
return r.r.Read(p)
}
// NewReader gets a context-aware io.Reader.
func NewReader(ctx context.Context, r io.Reader) io.Reader {
return &readerCtx{ctx: ctx, r: r}
}
I have the following code:
package main
import "net"
import "fmt"
import "bufio"
func main() {
conn, _ := net.Dial("tcp", "irc.freenode.net:6667")
reader := bufio.NewReader(conn)
go func() {
str, err := reader.ReadString('\n')
if err != nil {
// handle it
fmt.Println(err)
}
fmt.Println(str)
}()
}
If I don't have the code that reads from the buffer in a goroutine, it outputs a message like this, which is what I expect to happen:
:zelazny.freenode.net NOTICE * :*** Looking up your hostname...
However, having it inside a goroutine prints nothing.
Can someone explain why that is?
Your program will exit when the main() function finishes. This is likely to happen before your goroutine has time to run and print its output.
One option would be to have the main goroutine block reading from a channel, and have the goroutine write to the channel when it has completed its work.
Another common way to "wait for a goroutines end", is using WaitGroup:
http://golang.org/pkg/sync/#WaitGroup . You can use waitGroup.Add(1) for each started goroutine, then use waitGroup.Done() in each goroutine after it finishes. In the main function you can use waitGroup.Wait() and this will wait until waitGroup.Done() has been called for each added goroutine.
Write data to a channel ch at the end of goroutine and read data from ch out of goroutine can make the main function waiting for goroutine print message.
Here is an example:
package main
import "net"
import "fmt"
import "bufio"
func main() {
conn, _ := net.Dial("tcp", "irc.freenode.net:6667")
reader := bufio.NewReader(conn)
ch := make(chan byte, 1)
go func() {
str, err := reader.ReadString('\n')
if err != nil {
// handle it
fmt.Println(err)
}
fmt.Println(str)
ch <- 1
}()
<-ch
}
You need to add a time delay like time.Sleep(3 * time.Second) (this waits 3 seconds).
The goroutine closes when the program terminates. I had the exact same problem.
I wrote a short script to write a file concurrently.
One goroutine is supposed to write strings to a file while the others are supposed to send the messages through a channel to it.
However, for some really strange reason the file is created but no message is added to it through the channel.
package main
import (
"fmt"
"os"
"sync"
)
var wg sync.WaitGroup
var output = make(chan string)
func concurrent(n uint64) {
output <- fmt.Sprint(n)
defer wg.Done()
}
func printOutput() {
f, err := os.OpenFile("output.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666);
if err != nil {
panic(err)
}
defer f.Close()
for msg := range output {
f.WriteString(msg+"\n")
}
}
func main() {
wg.Add(2)
go concurrent(1)
go concurrent(2)
wg.Wait()
close(output)
printOutput()
}
The printOutput() goroutine is executed completely, if I tried to write something after the for loop it would actually get into the file. So this leads me to think that range output might be null
You need to have something taking from the output channel as it is blocking until something removes what you put on it.
Not the only/best way to do it but: I moved printOutput() to above the other funcs and run it as a go routine and it prevents the deadlock.
package main
import (
"fmt"
"os"
"sync"
)
var wg sync.WaitGroup
var output = make(chan string)
func concurrent(n uint64) {
output <- fmt.Sprint(n)
defer wg.Done()
}
func printOutput() {
f, err := os.OpenFile("output.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
defer f.Close()
for msg := range output {
f.WriteString(msg + "\n")
}
}
func main() {
go printOutput()
wg.Add(2)
go concurrent(1)
go concurrent(2)
wg.Wait()
close(output)
}
One of the reason why you get a null output is because channels are blocking for both send/receive.
According to your flow, the code snippet below will never reach wg.Done(), as sending channel is expecting a receiving end to pull the data out. This is a typical deadlock example.
func concurrent(n uint64) {
output <- fmt.Sprint(n) // go routine is blocked until data in channel is fetched.
defer wg.Done()
}
Let's examine the main func:
func main() {
wg.Add(2)
go concurrent(1)
go concurrent(2)
wg.Wait() // the main thread will be waiting indefinitely here.
close(output)
printOutput()
}
My take on the problem:
package main
import (
"fmt"
"os"
"sync"
)
var wg sync.WaitGroup
var output = make(chan string)
var donePrinting = make(chan struct{})
func concurrent(n uint) {
defer wg.Done() // It only makes sense to defer
// wg.Done() before you do something.
// (like sending a string to the output channel)
output <- fmt.Sprint(n)
}
func printOutput() {
f, err := os.OpenFile("output.txt", os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
if err != nil {
panic(err)
}
defer f.Close()
for msg := range output {
f.WriteString(msg + "\n")
}
donePrinting <- struct{}{}
}
func main() {
wg.Add(2)
go printOutput()
go concurrent(1)
go concurrent(2)
wg.Wait()
close(output)
<-donePrinting
}
Each concurrent function will deduct from the wait-group.
After the two concurrent goroutines finish, the wg.Wait() will unblock, and the next instruction (close(output)) will be executed. You have to wait for the two goroutines to finish before closing the channel. If, instead, you try the following:
go printOutput()
go concurrent(1)
go concurrent(2)
close(output)
wg.Wait()
you could end up with the close(output) instruction executing before any one of the concurrent goroutines concludes. If the channel closes before the concurrent goroutines run, they will crash at runtime, (while trying to write to a closed channel).
If, then, you don’t wait up for the printOutput() goroutine to finish, you could actually quit main() before printOutput() has gotten the chance to finish writing to its file.
Because I want to wait for the printOutput() goroutine to finish before I quit the program, I also created a separate channel just to signal that printOutput() is done.
The <-donePrinting instruction blocks until main receives something over the donePrinting channel.
Once main receives anything (even the empty structure that printOutput() sends), it will unblock and run to conclusion.
https://play.golang.org/p/nXJoYLI758m