Golang: Implementing a cron / executing tasks at a specific time - go

I have been looking around for examples on how to implement a function that allows you to execute tasks at a certain time in Go, but I couldn't find anything.
I implemented one myself and I am sharing it in the answers, so other people can have a reference for their own implementation.

This is a general implementation, which lets you set:
interval period
hour to tick
minute to tick
second to tick
UPDATED: (the memory leak was fixed)
import (
"fmt"
"time"
)
const INTERVAL_PERIOD time.Duration = 24 * time.Hour
const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 00
const SECOND_TO_TICK int = 03
type jobTicker struct {
timer *time.Timer
}
func runningRoutine() {
jobTicker := &jobTicker{}
jobTicker.updateTimer()
for {
<-jobTicker.timer.C
fmt.Println(time.Now(), "- just ticked")
jobTicker.updateTimer()
}
}
func (t *jobTicker) updateTimer() {
nextTick := time.Date(time.Now().Year(), time.Now().Month(),
time.Now().Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
if !nextTick.After(time.Now()) {
nextTick = nextTick.Add(INTERVAL_PERIOD)
}
fmt.Println(nextTick, "- next tick")
diff := nextTick.Sub(time.Now())
if t.timer == nil {
t.timer = time.NewTimer(diff)
} else {
t.timer.Reset(diff)
}
}

In case someone drops in on this question searching for a quick solution.
I found a neat library that makes it really easy to schedule jobs.
Link: https://github.com/jasonlvhit/gocron
The API is pretty straightforward:
import (
"fmt"
"github.com/jasonlvhit/gocron"
)
func task() {
fmt.Println("Task is being performed.")
}
func main() {
s := gocron.NewScheduler()
s.Every(2).Hours().Do(task)
<- s.Start()
}

the answer provided by #Daniele B is not good enough, as #Caleb says, that implementation leaks memory, because each time we create a new ticker, the old one will never be released.
so I wrap the time.timer, and reset it everytime, a example here:
package main
import (
"fmt"
"time"
)
const INTERVAL_PERIOD time.Duration = 24 * time.Hour
const HOUR_TO_TICK int = 23
const MINUTE_TO_TICK int = 21
const SECOND_TO_TICK int = 03
type jobTicker struct {
t *time.Timer
}
func getNextTickDuration() time.Duration {
now := time.Now()
nextTick := time.Date(now.Year(), now.Month(), now.Day(), HOUR_TO_TICK, MINUTE_TO_TICK, SECOND_TO_TICK, 0, time.Local)
if nextTick.Before(now) {
nextTick = nextTick.Add(INTERVAL_PERIOD)
}
return nextTick.Sub(time.Now())
}
func NewJobTicker() jobTicker {
fmt.Println("new tick here")
return jobTicker{time.NewTimer(getNextTickDuration())}
}
func (jt jobTicker) updateJobTicker() {
fmt.Println("next tick here")
jt.t.Reset(getNextTickDuration())
}
func main() {
jt := NewJobTicker()
for {
<-jt.t.C
fmt.Println(time.Now(), "- just ticked")
jt.updateJobTicker()
}
}

I have created a package that actually supports crontab syntax if you are familiar with it, for example:
ctab := crontab.New()
ctab.AddJob("*/5 * * * *", myFunc)
ctab.AddJob("0 0 * * *", myFunc2)
Package link: https://github.com/mileusna/crontab

This is another general implementation without need for a third party library.
Disclaimer: This implementation works with UTC. For managing timezones it has to be modified.
Run a func once a day at noon.
Period: time.Hour * 24
Offset: time.Hour * 12
Run a func twice a day at 03:40 (00:00 + 03:40) and 15:40 (12:00 + 03:40).
Period: time.Hour * 12
Offset: time.Hour * 3 + time.Minute * 40
Updated (2020-01-28):
Changes:
context.Context can be used for cancellation, makes it testable.
time.Ticker removes the need for calculating the time of the next execution.
package main
import (
"context"
"time"
)
// Schedule calls function `f` with a period `p` offsetted by `o`.
func Schedule(ctx context.Context, p time.Duration, o time.Duration, f func(time.Time)) {
// Position the first execution
first := time.Now().Truncate(p).Add(o)
if first.Before(time.Now()) {
first = first.Add(p)
}
firstC := time.After(first.Sub(time.Now()))
// Receiving from a nil channel blocks forever
t := &time.Ticker{C: nil}
for {
select {
case v := <-firstC:
// The ticker has to be started before f as it can take some time to finish
t = time.NewTicker(p)
f(v)
case v := <-t.C:
f(v)
case <-ctx.Done():
t.Stop()
return
}
}
}
Original:
package main
import (
"time"
)
// Repeat calls function `f` with a period `d` offsetted by `o`.
func Repeat(d time.Duration, o time.Duration, f func(time.Time)) {
next := time.Now().Truncate(d).Add(o)
if next.Before(time.Now()) {
next = next.Add(d)
}
t := time.NewTimer(next.Sub(time.Now()))
for {
v := <-t.C
next = next.Add(d)
t.Reset(next.Sub(time.Now()))
f(v)
}
}

I'm using https://github.com/ehsaniara/gointerlock. It's also supported in distributed systems and has a builtin distributer lock (Redis)
import (
"context"
"fmt"
"github.com/ehsaniara/gointerlock"
"log"
"time"
)
var job = gointerlock.GoInterval{
Interval: 2 * time.Second,
Arg: myJob,
}
err := job.Run(ctx)
if err != nil {
log.Fatalf("Error: %s", err)
}
func myJob() {
fmt.Println(time.Now(), " - called")
}

Related

time.Unix(t, offset) gives same time?

Trying to obtain successive timestamps with below Go code
package main
import "fmt"
import "time"
func main () {
ts := int64(1500000000) // start
for i := int64(0); i<1e6; i = i + 1000 { // successively add 1e3
t := time.Unix(ts, i) // Get start + i
fmt.Printf("%d %02d:%02d\n", ts+i, t.Hour(), t.Minute())
}
}
, output remains like
....
1500995000 04:40
1500996000 04:40
1500997000 04:40
1500998000 04:40
1500999000 04:40
Please, what is the problem here ? Why the hour:minute doesn't vary ? (04:40)
go version go1.15.6 linux/amd64
You are changing the nanoseconds and printing minutes...
https://pkg.go.dev/time#go1.16.7#Unix
func Unix(sec int64, nsec int64) Time
Check this:
https://play.golang.org/p/_Ywn9S5Khch
package main
import "fmt"
import "time"
func main () {
ts := int64(1500000000) // start
for i := int64(0); i<1e6; i = i + 1000 { // successively add 1e3
t := time.Unix(ts, i) // Get start + i
fmt.Printf("%d %02d:%02d ---- %2d\n", ts+i, t.Hour(), t.Minute(), t.UnixNano())
}
}

How to start a cron job at regular intervals starting from a time in the past

I would like to build a program which runs a number of cron jobs which start at a time which, in general, is in the past. Here is a simplified example using gocron:
package main
import (
"time"
"github.com/jasonlvhit/gocron"
"github.com/sirupsen/logrus"
)
// This slice would be obtained from persistent storage
var startTimes = []time.Time{
time.Now().Add(-4 * time.Second),
time.Now().Add(-3 * time.Second),
}
func format(t time.Time) string {
return t.Format("15:04:05")
}
func notify(startTime time.Time) {
logrus.WithField("time", format(time.Now())).Infof("I started at %s\n", format(startTime))
}
func main() {
for _, startTime := range startTimes {
gocron.Every(10).Seconds().From(&startTime).Do(notify, startTime)
}
logrus.Infof("Starting at %s...\n", format(time.Now()))
<-gocron.Start()
}
If I run this, I get the following output:
INFO[0000] Starting at 00:30:54...
INFO[0010] I started at 00:30:50 fields.time="00:31:04"
INFO[0010] I started at 00:30:51 fields.time="00:31:04"
What I observe is that all the events are happening at once, 10 seconds after I start the program.
However, since the startTimes are 4 and 3 seconds before the program starts, what I would like is for the events to occur 6 and 7 seconds after the program starts (and every 10 seconds after that), respectively.
Is there a way to do this with gocron or some other tool?
This seems like just simple math:
interval := 10 * time.Second
nextTime := time.Now().Add((time.Since(startTime) + interval) % interval)
gocron.Every(10).Seconds().From(&nextTime).Do(notify, nextTime)
https://play.golang.org/p/pwEZqy_LUuk
We can try to leverage a simple ticker, it is not a complete solution but should be easy enough to adapt.
package main
import (
"fmt"
"time"
)
func ticker(period time.Duration, length ...time.Duration) <-chan time.Time {
ticker := time.NewTicker(period)
if len(length) > 0 {
done := make(chan bool)
go func() {
time.Sleep(period + length[0])
done <- true
}()
go func() {
<-done
ticker.Stop()
}()
}
return ticker.C
}
func main() {
t1 := ticker(6*time.Second, 4*time.Second)
t2 := ticker(7*time.Second, 3*time.Second)
t3 := ticker(10 * time.Second)
for {
select {
case t1 := <-t1:
fmt.Println("t1: ", t1)
case t2 := <-t2:
fmt.Println("t2: ", t2)
case t3 := <-t3:
fmt.Println("t3: ", t3)
}
}
}

How to always get the latest value from a Go channel?

I'm starting out with Go and I'm now writing a simple program which reads out data from a sensor and puts that into a channel to do some calculations with it. I now have it working as follows:
package main
import (
"fmt"
"time"
"strconv"
)
func get_sensor_data(c chan float64) {
time.Sleep(1 * time.Second) // wait a second before sensor data starts pooring in
c <- 2.1 // Sensor data starts being generated
c <- 2.2
c <- 2.3
c <- 2.4
c <- 2.5
}
func main() {
s := 1.1
c := make(chan float64)
go get_sensor_data(c)
for {
select {
case s = <-c:
fmt.Println("the next value of s from the channel: " + strconv.FormatFloat(s, 'f', 1, 64))
default:
// no new values in the channel
}
fmt.Println(s)
time.Sleep(500 * time.Millisecond) // Do heavy "work"
}
}
This works fine, but the sensor generates a lot of data, and I'm always only interested in the latest data. With this setup however, it only reads out the next item with every loop, which means that if the channel at some point contains 20 values, the newest value only is read out after 10 seconds.
Is there a way for a channel to always only contain one value at a time, so that I always only get the data I'm interested in, and no unnecessary memory is used by the channel (although the memory is the least of my worries)?
Channels are best thought of as queues (FIFO). Therefore you can't really skip around. However there are libraries out there that do stuff like this: https://github.com/cloudfoundry/go-diodes is an atomic ring buffer that will overwrite old data. You can set a smaller size if you like.
All that being said, it doesn't sound like you need a queue (or ring buffer). You just need a mutex:
type SensorData struct{
mu sync.RWMutex
last float64
}
func (d *SensorData) Store(data float64) {
mu.Lock()
defer mu.Unlock()
d.last = data
}
func (d *SensorData) Get() float64 {
mu.RLock()
defer mu.RUnlock()
return d.last
}
This uses a RWMutex which means many things can read from it at the same time while only a single thing can write. It will store a single entry much like you said.
No. Channels are FIFO buffers, full stop. That is how channels work and their only purpose. If you only want the latest value, consider just using a single variable protected by a mutex; write to it whenever new data comes in, and whenever you read it, you will always be reading the latest value.
Channels serves a specific purpose. You might want to use a code that is inside a lock and update the variable whenever new value is to be set.
This way reciever will always get the latest value.
You cannot get that from one channel directly, but you can use one channel per value and get notified when there are new values:
package main
import (
"fmt"
"strconv"
"sync"
"time"
)
type LatestChannel struct {
n float64
next chan struct{}
mu sync.Mutex
}
func New() *LatestChannel {
return &LatestChannel{next: make(chan struct{})}
}
func (c *LatestChannel) Push(n float64) {
c.mu.Lock()
c.n = n
old := c.next
c.next = make(chan struct{})
c.mu.Unlock()
close(old)
}
func (c *LatestChannel) Get() (float64, <-chan struct{}) {
c.mu.Lock()
n := c.n
next := c.next
c.mu.Unlock()
return n, next
}
func getSensorData(c *LatestChannel) {
time.Sleep(1 * time.Second)
c.Push(2.1)
time.Sleep(100 * time.Millisecond)
c.Push(2.2)
time.Sleep(100 * time.Millisecond)
c.Push(2.3)
time.Sleep(100 * time.Millisecond)
c.Push(2.4)
time.Sleep(100 * time.Millisecond)
c.Push(2.5)
}
func main() {
s := 1.1
c := New()
_, hasNext := c.Get()
go getSensorData(c)
for {
select {
case <-hasNext:
s, hasNext = c.Get()
fmt.Println("the next value of s from the channel: " + strconv.FormatFloat(s, 'f', 1, 64))
default:
// no new values in the channel
}
fmt.Println(s)
time.Sleep(250 * time.Millisecond) // Do heavy "work"
}
}
If you do not need the notify about new value, you can try to read Channels inside channels pattern in Golang.
Try this package https://github.com/subbuv26/chanup
It allows the producer to update the channel with latest value, which replaces the latest value. And produces does not get blocked. (with this, stale values gets overridden).
So, on the consumer side, always only the latest item gets read.
import "github.com/subbuv26/chanup"
ch := chanup.GetChan()
_ := ch.Put(testType{
a: 10,
s: "Sample",
})
_ := ch.Update(testType{
a: 20,
s: "Sample2",
})
// Continue updating with latest values
...
...
// On consumer end
val := ch.Get()
// val contains latest value
There is another way to solve this problem (trick)
sender work faster: sender remove channel if channel_length > 1
go func() {
for {
msg:=strconv.Itoa(int(time.Now().Unix()))
fmt.Println("make: ",msg," at:",time.Now())
messages <- msg
if len(messages)>1{
//remove old message
<-messages
}
time.Sleep(2*time.Second)
}
}()
receiver work slower:
go func() {
for {
channLen :=len(messages)
fmt.Println("len is ",channLen)
fmt.Println("received",<-messages)
time.Sleep(10*time.Second)
}
}()
OR, we can delete old message from receiver side
(read message like delete it)
There is an elegant channel-only solution. If you're OK with adding one more channel and goroutine - you can introduce a buferless channel and a goroutine that tries to send the latest value from your channel to it:
package main
import (
"fmt"
"time"
)
func wrapLatest(ch <-chan int) <-chan int {
result := make(chan int) // important that this one i unbuffered
go func() {
defer close(result)
value, ok := <-ch
if !ok {
return
}
LOOP:
for {
select {
case value, ok = <-ch:
if !ok {
return
}
default:
break LOOP
}
}
for {
select {
case value, ok = <-ch:
if !ok {
return
}
case result <- value:
if value, ok = <-ch; !ok {
return
}
}
}
}()
return result
}
func main() {
sendChan := make(chan int, 10) // may be buffered or not
for i := 0; i < 10; i++ {
sendChan <- i
}
go func() {
for i := 10; i < 20; i++ {
sendChan <- i
time.Sleep(time.Second)
}
close(sendChan)
}()
recvChan := wrapLatest(sendChan)
for i := range recvChan {
fmt.Println(i)
time.Sleep(time.Second * 2)
}
}

How do you time a function in Go and return its runtime in milliseconds?

How do you time a function in Go and return its runtime in milliseconds?
Go's defer makes this trivial.
In Go 1.x, define the following functions:
func trace(s string) (string, time.Time) {
log.Println("START:", s)
return s, time.Now()
}
func un(s string, startTime time.Time) {
endTime := time.Now()
log.Println(" END:", s, "ElapsedTime in seconds:", endTime.Sub(startTime))
}
After that, you get Squeaky Clean one line elapsed time log messages:
func someFunction() {
defer un(trace("SOME_ARBITRARY_STRING_SO_YOU_CAN_KEEP_TRACK"))
//do a bunch of stuff here...
}
The clever magic is that the trace() is called at the beginning of the function, but the un() is deferred to the end. It's not atomic-clock accurate, due to the log statements, but if you need more accuracy, this kind of pattern is one of Go's marshmallowy good strengths.
EDIT:
This answer originally used legacy time package API. Reproduced here for historical value only:
For use w/ Go versions prior to 12-01-2011 weekly:
func trace(s string) (string, int64) {
log.Println("START:", s)
return s, time.Nanoseconds()
}
func un(s string, startTime int64) {
endTime := time.Nanoseconds()
log.Println(" END:", s, "ElapsedTime in seconds:", float32(endTime-startTime)/1E9)
}
Use the Go testing package to benchmark the function. For example,
package main
import (
"fmt"
"testing"
)
// the function to be benchmarked
func Function(n int) int64 {
n64 := int64(n)
return n64 * n64
}
func BenchmarkFunction(b *testing.B) {
n := 42
for i := 0; i < b.N; i++ {
_ = Function(n)
}
}
func main() {
br := testing.Benchmark(BenchmarkFunction)
fmt.Println(br)
}
Output:
500000000 4.22 ns/op
You can also use the Go gotest command to run benchmarks.
Another easy way can be:
import (
"fmt"
"time"
)
start := time.Now()
// some computation
elapsed := time.Since(start)
fmt.Println(elapsed)
which will output something like 359.684612ms
Perhaps you can also use a Duration (elapsed) for this...looks a little bit nicer.
func trace(s string) (string, time.Time) {
log.Printf("trace start: %s\n", s)
return s, time.Now()
}
func un(s string, startTime time.Time) {
elapsed := time.Since(startTime)
log.Printf("trace end: %s, elapsed %f secs\n", s, elapsed.Seconds())
}
There are several options for timestamping and timers in the time package. See the documentation here: http://golang.org/pkg/time/

What can you do in 30 lines of Go? Can you create a useful, complete program that demonstrates its features? [closed]

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
So, the big buzz in the last few days is Go, the new language from Google. Assuming you're all obsessive programming language geeks like me, you've all downloaded it, built it, and run your "Hello, 世界" program (isn't it nice using a language written by the inventors of UTF-8?). You've all read the tutorial, Effective Go, and some of the other docs.
Now, what are you going to do with it?
I'd like to see some demos that show off the power of Go. What can you do in a brief program? Show off your best example code. While the true measure of a language can't really be taken until you've written and maintained a large codebase with a team of many programmers over the course of a project with changing requirements, seeing how much you can do in a limited amount of code does help to demonstrate the expressive power of a language. I'd like to see short, complete programs that truly exercise the unique new features of Go; not just snippets or "Hello, World".
So, post some cool code you've written with Go. Take advantage of its unique features, like its goroutines and channels for concurrency, or its interface based type system. Can you write a primitive chat server, or cool IRC bot? Implement a parallel Mandelbrot set that scales to many cores? Write an interpreter for some tiny language? And can you do it all in 30 lines?
I chose 30 arbitrarily as about as much as you can fit into a Stack Overflow code block without it overflowing and getting a scroll bar; it should be enough to do something interesting without golfing too much, but short enough to keep everyone's attention for a quick demo. For instance, with just a bit of reformatting, the example web server should be able to fit (not counting the data).
Show us your Go code!
This is a web proxy I wrote to provide unauthenticated access to a web service that required HTTP basic auth. I needed it for an internal thingy (and still use it):
package main
import (
"flag"
"log"
"net/http"
"net/url"
)
var target = flag.String("target", "http://www.google.com/", "Where to go.")
var addr = flag.String("listen", ":12345", "Address/port on which to listen.")
var auth = flag.String("auth", "", "Authorization header to add (optional).")
func main() {
flag.Parse()
targetUrl, uerr := url.Parse(*target)
if uerr != nil {
log.Fatalf("Error parsing target ``%s'': ", target, uerr.String())
}
proxy := http.ReverseProxy{Director: func(req *http.Request) {
req.URL.Scheme = targetUrl.Scheme
req.URL.Host = targetUrl.Host
req.Host = targetUrl.Host
if *auth != "" {
req.Header.Set("Authorization", *auth)
}
}}
log.Fatal(http.ListenAndServe(*addr, &proxy))
}
This makes a PNG (on stdout) of a clock face showing the current time. It's barely golfed to fit thirty lines, so the code is not quite as clean as it should be.
package main
import ("image"; "image/png"; "math"; "bufio"; "os"; "time")
const clock_size = 200;
const radius = clock_size / 3;
var colour image.RGBAColor;
func circle (clock *image.RGBA) {
for angle := float64(0); angle < 360; angle++ {
radian_angle := math.Pi * 2 * angle / 360;
x := radius * math.Sin (radian_angle) + clock_size/2;
y := radius * math.Cos (radian_angle) + clock_size/2;
clock.Set (int (x), int (y), colour);}}
func hand (clock *image.RGBA, angle float64, length float64) {
radian_angle := math.Pi * 2 * angle;
x_inc := math.Sin (radian_angle);
y_inc := -math.Cos (radian_angle);
for i := float64(0); i < length; i++ {
x := i * x_inc + clock_size/2;
y := i * y_inc + clock_size/2;
clock.Set (int (x), int (y), colour);}}
func main () {
clock := image.NewRGBA (clock_size, clock_size);
colour.A = 255;
circle (clock);
time := time.LocalTime ();
hand (clock, (float64(time.Hour) + float64(time.Minute)/60)/12, radius*0.6); // hour hand
hand (clock, (float64(time.Minute) + float64(time.Second)/60)/60, radius*0.8); // minute hand
out := bufio.NewWriter(os.Stdout);
defer out.Flush();
png.Encode(out, clock);
}
Run it like
8.out > clock.png
Notice all those float64 casts? I've NEVER seen a language as strict as Go about types.
This is the same code fixed with go fix (and some manual tweaking) and then automatically formatted using go fmt. Some newlines where inserted manually.
package main
import (
"bufio"
"image"
"image/color"
"image/png"
"math"
"os"
"time"
)
const clock_size = 200
const radius = clock_size / 3
var colour color.RGBA
func circle(clock *image.RGBA) {
for angle := float64(0); angle < 360; angle++ {
radian_angle := math.Pi * 2 * angle / 360
x := radius*math.Sin(radian_angle) + clock_size/2
y := radius*math.Cos(radian_angle) + clock_size/2
clock.Set(int(x), int(y), colour)
}
}
func hand(clock *image.RGBA, angle float64, length float64) {
radian_angle := math.Pi * 2 * angle
x_inc := math.Sin(radian_angle)
y_inc := -math.Cos(radian_angle)
for i := float64(0); i < length; i++ {
x := i*x_inc + clock_size/2
y := i*y_inc + clock_size/2
clock.Set(int(x), int(y), colour)
}
}
func main() {
clock := image.NewRGBA(image.Rect(0, 0, clock_size, clock_size))
colour.A = 255
circle(clock)
time := time.Now()
hand(clock, (float64(time.Hour())+float64(time.Minute())/60)/12, radius*0.6) // hour hand
hand(clock, (float64(time.Minute())+float64(time.Second())/60)/60, radius*0.8) // minute hand
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
png.Encode(out, clock)
}
OK, I'll get the ball rolling. Here's my first Go program. It's a very primitive chat server, and fits in 30 lines of 80 characters if I compress it down a bit; formatted with gofmt, it is 60 lines. It listens on a hard coded port (4242), does basically no error handling, and doesn't handle client disconnection other than stopping trying to read from a client if it gets an error.
package main
import ("net";"container/vector";"bufio";"strings")
type client struct { conn net.Conn; send chan string; receive chan string }
func main() {
if listener, err := net.Listen("tcp", "0.0.0.0:4242"); err == nil {
master := make(chan string, 100);
clients := vector.New(0);
go runServer(master, clients);
for {
if conn, err := listener.Accept(); err == nil {
c := client{ conn, master, make(chan string, 100) };
clients.Push(c);
go runClient(c);
} else { break } } } }
func runServer(master chan string, clients *vector.Vector) {
for {
message := <-master;
clients.Do(func (c interface{}) { c.(client).receive <- message }); } }
func runClient(c client) {
input := make(chan string, 10);
go readLines(c, input);
for {
select {
case inMessage := <-input: c.send <- inMessage;
case outMessage := <-c.receive: c.conn.Write(strings.Bytes(outMessage));
} } }
func readLines(c client, input chan string) {
reader := bufio.NewReader(c.conn);
for { if line, err := reader.ReadString('\n'); err == nil
{ input <- line; } else { break } } }
Build and run with:
$ 6g server.go
$ 6l -o server server.6
$ ./server
And then in a few other terminals, connect with
$ nc localhost 4242
I really like go's channels and the select statement, so here's something that shows how easy it is to express the "go and get as many things as possible within a certain time" concept.
This generates as many random numbers as possible within 300 milliseconds and returns the biggest one generated in that time.
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
timeout := time.After(300 * time.Millisecond)
numbers := make(chan int) // This channel will be used
var numberCount int = 0
var maxNumber int = 0
// Start putting random numbers on the numbers channel
go func() {
for {
numbers <- rand.Int()
}
}()
for {
select {
case <- timeout:
fmt.Printf("%v numbers generated. Max number found: %v.\n", numberCount, maxNumber)
return
case number := <- numbers:
numberCount++
if number > maxNumber {
maxNumber = number
}
}
}
}
I copied this from somewhere. Fairly simple, but shows some features.
package main
import ("fmt"; "os" ;"bufio";"io")
func main() {
if len(os.Args) < 2 {
fmt.Printf("usage: catfile \n")
} else if pFile, err := os.OpenFile(os.Args[1], os.O_RDONLY, 0); err != nil {
fmt.Printf("error opening file : %s\n", err)
} else {
defer pFile.Close()
displayFile(pFile)
}
}
func displayFile(pFile *os.File) {
oReader := bufio.NewReader(pFile);
for {
if sLine, err := oReader.ReadString('\n'); err == nil {
fmt.Printf("%s", sLine)
} else {
if err != io.EOF {fmt.Printf("error reading file : %s\n", err)}
break
}
}
}

Resources