I use a Ticker to execute tasks periodically, but I've got some problems when changing it. I'll change the ticker to a new one on receiving some messages and change the interval. Here's the sample code which will repro this problem:
package main
import (
"fmt"
"time"
)
type A struct {
ticker *time.Ticker
}
func (a *A) modify() {
a.ticker.Stop()
a.ticker = time.NewTicker(time.Second)
}
func main() {
a := new(A)
a.ticker = time.NewTicker(time.Second)
go func() {
for {
select {
case <-a.ticker.C:
fmt.Println("now")
go a.modify()
/*
default:
//fmt.Println("default")
time.Sleep(time.Millisecond * 100)
*/
}
}
}()
time.Sleep(time.Second * 60)
}
"now" will be printed only once. But it will be printed continously if I remove the "go", like this:
package main
import (
"fmt"
"time"
)
type A struct {
ticker *time.Ticker
}
func (a *A) modify() {
a.ticker.Stop()
a.ticker = time.NewTicker(time.Second)
}
func main() {
a := new(A)
a.ticker = time.NewTicker(time.Second)
go func() {
for {
select {
case <-a.ticker.C:
fmt.Println("now")
a.modify()
/*
default:
//fmt.Println("default")
time.Sleep(time.Millisecond * 100)
*/
}
}
}()
time.Sleep(time.Second * 60)
}
Also, if I leave the default clause un-commented, "now" can be printed continously.
Can anyone explain how would this happen?
The problem is that goroutine runs asynchronously.
your code behave like this when using a.modify():
get tick from a.ticker.C
stop old ticker, and create new a.ticker.C
wait for a.ticker.C using select
In this case, newly created a.ticker.C in 2. is identical to channel waiting in 3.
If you do 2. in goroutine. it may be done in following order
get tick from a.ticker.C
wait for a.ticker.C using select
stop old ticker, and create new a.ticker.C
In this case channel waiting in 2. is different from newly created channel in 3. .
Since selecting channel is old one which is stopped, it never gets any tick.
You can confirm this behavior inserting some fmt.Printf and watch for the address of a.ticker.C.
func (a *A) modify() {
a.ticker.Stop()
fmt.Printf("ticker stopped: %p\n", &a.ticker.C)
a.ticker = time.NewTicker(time.Second)
fmt.Printf("new ticker created: %p\n", &a.ticker.C)
}
func main() {
a := new(A)
a.ticker = time.NewTicker(time.Second)
go func() {
for {
fmt.Printf("waiting for ticker: %p\n", &a.ticker.C)
select {
....
with a.modify():
waiting for ticker: 0xc420010100
ticker stopped: 0xc420010100
new ticker created: 0xc420068000
waiting for ticker: 0xc420068000
ticker stopped: 0xc420068000
new ticker created: 0xc420068080
waiting for ticker: 0xc420068080
with go a.modify():
waiting for ticker: 0xc420010100
waiting for ticker: 0xc420010100
ticker stopped: 0xc420010100
new ticker created: 0xc420066040
you can see that with go a.modify() you are not waiting for a newly created channel.
UPDATE for behavior of default:
When using default: with go a.modify(), it will behave like this.
wait for a.ticker.C, got tick, call go a.modify() which does 3.
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
stop old ticker, update a.ticker.C
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
wait for a.ticker.C, got nothing, so fallback to default and sleep 100ms.
.....
wait for a.ticker.C, got tick, call go a.modify()
The point is that for{} loop can keep on going even when you got nothing from a.ticker.C.
You can confirm the behavior with same code.
waiting ticker: 0xc420010100 <-- 1.
now <-- 1.
waiting ticker: 0xc420010100 <-- 2.
default <-- 2.
ticker stopped: 0xc420010100 <-- 3.
new ticker created: 0xc420066240 <-- 3.
waiting ticker: 0xc420066240 <-- 4.
default <-- 4.
Related
In this example, we have a worker. The idea here is simulate clean shutdown of all go routines based on a condition.
In this case, go routines get spun - based on workers count. Each go routine reads the channel, does some work and sends output to the outputChannel.
The main go routine reads this output and prints it. To simulate a stop condition, the doneChannel is closed. Expected outcome is that select inside each go routine will pick this up and execute return which in turn will call the defer println. The actual output is that it never gets called and main exits.
Not sure what's the reason behind this.
package main
import (
"log"
"time"
)
const jobs = 100
const workers = 1
var timeout = time.After(5 * time.Second)
func main() {
doneChannel := make(chan interface{})
outputChannel := make(chan int)
numberStream := generator()
for i := 1; i <= workers; i++ {
go worker(doneChannel, numberStream, outputChannel)
}
// listen for output
loop:
for {
select {
case i := <-outputChannel:
log.Println(i)
case <-timeout:
// before you timeout cleanup go routines
break loop
}
}
close(doneChannel)
time.Sleep(5 * time.Second)
log.Println("main exited")
}
func generator() <-chan int {
defer log.Println("generator completed !")
c := make(chan int)
go func() {
for i := 1; i <= jobs; i++ {
c <- i
}
defer close(c)
}()
return c
}
func worker(done <-chan interface{}, c <-chan int, output chan<- int) {
// this will be a go routine
// Do some work and send results to output Channel.
// Incase if the done channel is called kill the go routine.
defer log.Println("go routines exited")
for {
select {
case <-done:
log.Println("here")
return
case i := <-c:
time.Sleep(1 * time.Second) // worker delay
output <- i * 100
}
}
}
When your main loop finishes during the timeout, you continue your program and
Close done channel
Print message
Exit
There is no reason to wait for any goroutine to process the signal of this channel.
If you add a small sleep you will see some messages
In real scenarios we use a waitgroup to be sure all goroutine finish properly
I'm trying to get a better understanding of channel in GO.
Want 5 routines to be running at all times. At specific times during the first routine, I want to try to start another routine. Assuming 5 routines are already running, I want to queue up the next routine and run it as soon as one of the other routines has been completed.
My logic was call pass a message to spawner, check to see if there are 5 processes already running, if so, keep waiting until there isn't, and start up. From what I can tell, is is p.complete <- struct{}{} isn't working as expecting and removing a process. It works fine outside of the go routine.
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
)
type ProcessManager struct {
spawner chan struct{}
complete chan struct{}
process int
}
func NewProcessManager() *ProcessManager {
return &ProcessManager{
spawner: make(chan struct{}),
complete: make(chan struct{}),
process: 0,
}
}
func (p *ProcessManager) Run(limit int) {
for {
select {
case <-p.spawner:
for {
if p.process <= limit {
fmt.Println("breaking for new process")
break
}
time.Sleep(time.Second * 10)
}
p.process++
go func() {
fmt.Println("+ Starting goroutine")
p.spawner <- struct{}{}
time.Sleep(time.Second * 2)
fmt.Println("- Stopping goroutine")
p.complete <- struct{}{}
}()
case <-p.complete:
fmt.Println("complete")
p.process--
}
}
}
func main() {
interruptChannel := make(chan os.Signal, 1)
signal.Notify(interruptChannel, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
pm := NewProcessManager()
go pm.Run(5)
pm.spawner <- struct{}{}
<-interruptChannel
}
Managed to solve this by switching process to process: make(chan int, 4)
Then I just used this to block instead of the for loop p.process <- 1
and then use this to mark a routine as completed <-p.process
The length of the channel (4) will determine the max number of processed allowed to run at any given time. Updated test I provided below:
func NewProcessManager() *ProcessManager {
return &ProcessManager{
spawner: make(chan struct{}),
complete: make(chan struct{}),
process: make(chan int, 4),
}
}
func (p *ProcessManager) Run() {
for {
select {
case <-p.spawner:
p.process <- 1
go func() {
fmt.Println("+ Starting goroutine")
// do stuff before starting next routine
p.spawner <- struct{}{}
// do stuff rest of stuff
fmt.Println("- Stopping goroutine")
<-p.process
}()
}
}
The whole purpose of the ProcessManager in my eyes is to serialize access to the metadata keeping track of running processes. As such, it runs sequential.
case <-p.spawner:
for {
if p.process <= limit {
fmt.Println("breaking for new process")
break
}
time.Sleep(time.Second * 10)
}
In this section of code, the goroutine is sleeping. While sleeping, the complete case in the select statement cannot run.
Recall the following property of unbuffered channels:
By default, sends and receives block until the other side is ready.
Because the channels are unbuffered, this line of code must block until that complete case is triggered:
p.complete <- struct{}{}
I'm new to Go and the time package is a tad confusing for me. So I'm making a chat box and the idea is that when you send a message, the timer resets to 20 seconds, and if you don't respond by 20 seconds you get kicked out. The code I have works only if the person has typed something, but if they never input anything, they don't get kicked out. I tried applying Stop() before the for loop, but it doesn't work. Should I do all the timer before and after the loop rather than inside the for loop?
func ... {
timer := time.NewTimer(20 * time.Second)
for input.Scan() {
go func(){
<-timer.C
leaving <- ch
conn.Close()
}()
messages <- input.Text()
timer.Stop()
timer.Reset(20 * time.Second)
}
You can use time.AfterFunc; for example;
package main
import (
"fmt"
"time"
)
func main() {
timer := time.AfterFunc(time.Second*60, func() {
fmt.Printf("you're out!")
})
defer timer.Stop()
}
'cause whether user types something or not you can close the connection and send to leaving channel.
How do I run 10 threads, for 30 seconds each, and then return to the program execution? For example, I want
10 threads to be spawned and ran for 30 seconds.
Then all threads killed
Then second() to run (i.e. after all threads have finished executing)
So far I have the following however, when I do this, the threads (obviously) keep executing and CPU usage remains at 100% after 30s:
func main(){
for i := 0; i < 10; i++{
go thread()
}
time.Sleep(30 * time.Second)
second()
}
func thread() {
for {
// Do stuff
}
}
You could use Golang context. Here's some of my code when I was learning it.
package main
import (
"fmt"
"log"
"time"
"golang.org/x/net/context"
)
func main() {
someHandler()
}
func someHandler() {
//Create a new context with a timeout duration of six seconds. You also get a cancel function you can call early.
//You can also have context end at a certain time, instead of a timeout
ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(6))
for i := 0; i < 5; i++ {
go doStuff(ctx, i)
}
select {
case <- ctx.Done():
log.Println("Got a cancel request")
return
case <-time.After(time.Second * time.Duration(5)):
//Here I'm actually ending it earlier than the timeout with cancel().
log.Println("Timed out")
cancel()
}
}
func doStuff(ctx context.Context, num int) {
for {
select {
case <-ctx.Done():
fmt.Println("Done working")
return
default:
fmt.Println("go", num, "working")
}
time.Sleep(time.Second)
}
}
Outputs:
$ go run app.go
go 0 working
go 4 working
go 2 working
go 3 working
go 1 working
go 1 working
go 0 working
go 4 working
go 3 working
go 2 working
go 0 working
go 4 working
go 2 working
go 1 working
go 3 working
go 4 working
go 3 working
go 0 working
go 2 working
go 1 working
go 3 working
go 4 working
go 1 working
go 0 working
go 2 working
2016/10/01 23:25:23 Timed out
Use a wait group and a "done" channel to cancel running go routines and and synchronize their exits before calling second. Some really cool stuff on the topic can be read here https://blog.golang.org/pipelines.
package main
import (
"fmt"
"sync"
"time"
)
func main() {
//create a "done" channel that will be used to signal to all go routines running thread to quit
done := make(chan interface{})
//create a wait group to sync all go routines before continuing program execution
wg := new(sync.WaitGroup)
for i := 0; i < 10; i++ {
//increment waitgroup
wg.Add(1)
go thread(done, wg)
}
//zzzz...
time.Sleep(30 * time.Second)
//close the done channel
close(done)
//wait for all go routines to quit
wg.Wait()
//move on
second()
}
func thread(done chan interface{}, wg *sync.WaitGroup) {
defer wg.Done()
working := true
for working {
//use a select statement to do work until done channel is closed
select {
case <-done:
//end the loop
working = false
default:
fmt.Println("doing work...")
}
}
}
func second() {
fmt.Println("program complete")
}
Is there a way in which I can execute, for example
time.Sleep(time.Second * 5000) //basically a long period of time
and then "wake up" the sleeping goroutine when I wish to do so?
I saw that there is a Reset(d Duration) in Sleep.go but I'm unable to invoke it.. Any thoughts?
There isn't a way to interrupt a time.Sleep, however, you can make use of time.After, and a select statement to get the functionality you're after.
Simple example to show the basic idea:
package main
import (
"fmt"
"time"
)
func main() {
timeoutchan := make(chan bool)
go func() {
<-time.After(2 * time.Second)
timeoutchan <- true
}()
select {
case <-timeoutchan:
break
case <-time.After(10 * time.Second):
break
}
fmt.Println("Hello, playground")
}
http://play.golang.org/p/7uKfItZbKG
In this example, we're spawning a signalling goroutine to tell main to stop pausing. The main is waiting and listening on two channels, timeoutchan (our signal) and the channel returned by time.After. When it receives on either of these channels, it will break out of the select and continue execution.