I'm trying to write my own sleep function equivalent to time.Sleep using time.After in Go.
Here's the code. First attempt:
func Sleep(x int) {
msg := make(chan int)
msg := <- time.After(time.Second * x)
}
Second attempt:
func Sleep(x int) {
time.After(time.Second * x)
}
Both return errors, can someone explain to me how to write a sleep function equivalent to time.Sleep using time.After and if possible when do I use channel?
time.After() returns you a channel. And a value will be send on the channel after the specified duration.
So just receive a value from the returned channel, and the receive will block until the value is sent:
func Sleep(x int) {
<-time.After(time.Second * time.Duration(x))
}
Your errors:
In your first example:
msg := <- time.After(time.Second * x)
msg is already declared, and so the Short variable declaration := cannot be used. Also the recieved value will be of type time.Time, so you can't even assign it to msg.
In your second example you need a type conversion as x is of type int and time.Second is of type time.Duration, and time.After() expects a value of type time.Duration.
Related
I am going to show a simple compilable code snipped where I get weird behaviour: after I intentionally cause a panic in processData (because I pass nil-pointer) the sending to channel l.done is blocked!
package main
import (
"context"
"fmt"
"time"
)
type Loop struct {
done chan struct{}
}
type Result struct {
ID string
}
func NewLoop() *Loop {
return &Loop{
done: make(chan struct{}),
}
}
func (l *Loop) StartAsync(ctx context.Context) {
go func() {
defer func() {
l.done <- struct{}{} // BLOCKED! But I allocated it in NewLoop ctor
fmt.Sprintf("done")
}()
for {
/**/
var data *Result
l.processData(ctx, data) // passed nil
}
}()
}
func (l *Loop) processData(ctx context.Context, data *Result) {
_ = fmt.Sprintf("%s", data.ID) // intentional panic - OK
}
func main() {
l := NewLoop()
l.StartAsync(context.Background())
time.Sleep(10 * time.Second)
}
I can recover() a panic before sending to channel and I get correct error message.
What does happen with channel? It it was closed, I would get panic on sending to closed channel
It's blocking because there isn't anything receiving from the channel. Both the receive & the send operations on an initialized and unbuffered channel will block indefinitely if the opposite operation is missing. I.e. a send to a channel will block until another goroutine receives from that channel, and, likewise, a receive from a channel will block until another goroutine sends to that channel.
So basically
done := new(chan struct{})
done<-struct{}{}
will block forever unless there is another goroutine that receives from the channel, i.e. <-done. That's how channels are supposed to behave. That's how the languages specification defines their behaviour.
about the possible fixes :
given the name of your channel, you may want to run close(l.done) rather than l.done <- struct{}{}.
using a buffered channel and l.done <- struct{}{} on completion : only one call to <-l.done will be unblocked.
Suppose you have some code looking like :
l := NewLoop()
go func(){
<-l.done
closeLoggers()
}()
go func(){
<-l.done
closeDatabase()
}()
sending one item on the done channel will make that only one consumer will receive it, and in the above example only one of the two actions will be triggered when the loop completes.
using close(l.done) : once a channel is closed all calls to receive from it will proceed.
In the above example, all actions will proceed.
As a side note: there is no need for a buffer if you use a channel only for its "open/closed" state.
I am not clear about why the print statement in func a1 will not print anything if I remove <-result in func a2.
I think before we send something into result, the print statement should be executed, and <-result should not have some effect on that.
func a2(){
x := 3
result := make(chan int, 10)
input := make(chan int, 10)
go a1(x, input, result)
input <- 4
<-result
}
func a1(x int, input <-chan int, result chan<- int){
y := <-input
fmt.Println("hello", y)
result <- x
}
However, then I tried the following code: it will print hello no matter whether I have <-result or not.
func a2(){
x := 3
result := make(chan int, 10)
go a1(x, result)
<-result
}
func a1(x int, result chan<- int){
fmt.Println("hello")
result <- x
}
Can you explain this in a detailed way so that a beginner can understand this? It seems like input <-chan int this input channel is doing something that causes the difference.
Because without <-result, a2() returns and the program terminates, assuming a2() is the only thing main() does. Probably a1() never even wakes up, because input channel is buffered, and writing to it will not block. If you make it unbuffered, then a1() will wake up before a2() returns, but that still doesn't guarantee that println will run.
With <-result, a2() waits for a1() to read from the result channel, which is after println. That's the only safest way to ensure println runs.
I'm trying to build a function that I pass a channel to and when run in a go routine it will constantly post updates (in this instance, values of sin) to a channel. When data is sent the the channel, I then want to send it over a web socket.
func sineWave(value chan float64) {
var div float64
sinMult := 6.2839
i := 0
log.Println("started")
for {
div = (float64(i+1) / sinMult)
log.Println(math.Sin(div))
time.Sleep(100 * time.Millisecond)
value <- math.Sin(div)
// log.Println()
i++
if i == 45 {
i = 0
}
}
// log.Println(math.Sin(div * math.Pi))
}
It seems to get stuck at value <- main.Sin(div) stopping the rest of main() from running. How do i get sineWave to run indefinitely in the background and to print its output in another function as they arrive?
There is several mistakes in this code,
the value chan is never drained, so any write will block
the value chan is never closed, so any drain will be infinite
A channel must always be drained, a channel must be closed at some point.
Also, please post reproducible examples, otherwise it is difficult to diagnose the issue.
This is a slightly modified but working version of the OP code.
package main
import (
"fmt"
"math"
"time"
)
func sineWave(value chan float64) {
defer close(value) // A channel must always be closed by the writer.
var div float64
sinMult := 6.2839
i := 0
fmt.Println("started")
for {
div = (float64(i+1) / sinMult)
time.Sleep(100 * time.Millisecond)
value <- math.Sin(div)
i++
if i == 4 {
// i = 0 // commented in order to quit the loop, thus close the channel, thus end the main for loop
break
}
}
}
func main() {
value := make(chan float64)
go sineWave(value) // start writing the values in a different routine
// drain the channel, it will end the loop whe nthe channel is closed
for v := range value {
fmt.Println(v)
}
}
This function is called from a goroutine by passing as parameter m.
The value sent in m is the string: "01a" and the statement Switch does not recognize
func myfunc(m string, c chan string) {
defer close(c)
switch m {
case "01a":
msg_out = "NO PASS"
}
c <- msg_out
}
when set m the Switch works fine
func myfunc(m string, c chan string) {
defer close(c)
m = "01a"
switch m {
case "01a":
msg_out = "PASS"
}
c <- msg_out
}
I suspect the channel will introduce other hidden characters
It's not clear what your code is trying to do, the code provided is invalid.
You haven't shown any attempt to read from the channel in either example provided, both examples switch on a string then assign a new value to msg_out (which is not declared) and send that value down a channel.
Without the full code it's impossible to tell where you are going wrong, here is a simple example of sending text down a channel and reading it off again. Hopefully this will clarify the process and confirm that strings are sent over channels just fine.
If you still cant get it working, I think you will need to post full code example to get help.
Go playground
package main
import (
"log"
"time"
)
func main() {
//make the channel
strChan := make(chan string)
//launch a goroutine to listen on the channel
go func(strChan chan string) {
//loop waiting for input on channel
for {
select {
case myString := <-strChan:
// code to be run when a string is recieved here
// switch on actual contents of the string
switch myString {
case "I'm the expected string":
log.Println("got expected string")
default:
log.Println("not the string I'm looking for")
}
}
}
}(strChan)
//sleep for a bit then send a string on the channel
time.Sleep(time.Second * 2)
strChan <- "I'm the expected string"
//wait again, gives channel response a chance to happen
time.Sleep(time.Second * 2)
}
I'm just playing around with Go, taking it for a test drive so to speak. I'm having a problem where a go routine that is mean to receive 3 integers only seems to receive one.
type simpleFunction func() int
func run(fChan chan simpleFunction, result chan int) {
for{
select {
case fn := <-fChan:
fmt.Printf("sending: %d down result chan\n", fn())
result <- fn()
case <-time.After(time.Second * 2):
close(fChan)
}
}
}
func recieve(result chan int){
for {
select {
case x := <-result:
fmt.Printf("recieved: %d from result chan\n", x)
case <-time.After(time.Second * 2):
close(result)
}
}
}
So, as you can see the run routine receives functions, evaluates them, and then sends the result down the result channel.
Here's my main/test:
func main() {
fns := []simpleFunction{
func() int {return 1},
func() int {return 2},
func() int {return 3},
}
fChan := make(chan simpleFunction)
result := make(chan int)
go run(fChan, result)
go recieve(result)
for _, fn := range fns {
fmt.Printf("sending a function that returns: %d down function chan\n", fn())
fChan <- fn
}
}
And here's my output:
sending a function that returns: 1 down function chan
sending: 1 down result chan
recieved: 1 from result chan
sending a function that returns: 2 down function chan
sending a function that returns: 3 down function chan
sending: 2 down result chan
sending: 3 down result chan
So, as you can see, everything seems to go swimmingly for the first function, but it's not so hot afterwards. Any tips or suggestions?
There are a couple of issues with this code:
The program terminates when main returns. It does not wait for the run and receive goroutines to complete.
There's a race on closing the channels. There's no guarantee that the sender will top sending before the timeout.
If main does not exit, then the for { select { } } loops will spin forever printing zero values. Receive on a closed channel returns the zero value.