Go basic: Understanding Timers, Tickers, Worker Pools, Waitgroups, Rate Limiting in Go

we delve into the details related to Timers, Tickers, Worker Pools, Waitgroups, and Rate Limiting.

full lessons here👇:
https://programmerscareer.com/golang-basic-skill/

Generated by AI, there may be errors, for reference only

Topic: Timers

Timers represent a single event in the future. You tell the timer how long you want to wait, and it provides a channel that will notify you at that time. This is how you make a timer in Go:

1
2
3
timer := time.NewTimer(2 * time.Second)  
<-timer.C
fmt.Println("Timer fired")

In this example, we create a timer that is set to fire after 2 seconds. The <-timer.C line blocks the timer’s C channel until it sends a value indicating that the timer fired.

What if you want to stop the timer before it fires? You can do that with the Stop function:

1
2
3
4
5
6
7
8
9
10
timer := time.NewTimer(2 * time.Second)  
go func() {
<-timer.C
fmt.Println("Timer fired")
}()

stop := timer.Stop()
if stop {
fmt.Println("Timer stopped")
}

In this case, we stop the timer before it has a chance to fire.

Topic: Tickers

Unlike timers, tickers are not for one-time events — they’re for repeated tasks at regular intervals. Here’s how you can create a ticker in Go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ticker := time.NewTicker(500 * time.Millisecond)  
done := make(chan bool)

go func() {
for {
select {
case <-done:
return
case t := <-ticker.C:
fmt.Println("Tick at", t)
}
}
}()

time.Sleep(1600 * time.Millisecond)
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")

In this example, we are creating a ticker that ticks every 500 milliseconds. We then create an anonymous goroutine that listens on the ticker.Cchannel in a loop. Every time a tick happens, the goroutine prints the tick’s time.

After 1600 milliseconds, we stop the ticker and send a message on the donechannel to tell the goroutine to stop too.

Now that you’ve got a handle on timers and tickers, let’s talk about Worker Pools.

Topic: Waitgroups

In Go language, sync package provides WaitGroup. It’s designed to tackle a very common problem. In a situation, where we have n number of Goroutines. Now, we need to make sure that we proceed only after these nGoroutines finish their execution. This can easily be accomplished by WaitGroup.

You can think of a wait group like a token system or a bookkeeper, where each bookkeeper keeps track of the tasks to be done and at the end checks if all tasks are completed.

Consider this example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package main  

import (
"fmt"
"sync"
"time"
)

var wg sync.WaitGroup

func worker(msg string) {
// Schedule the call to WaitGroup's Done to tell we are done.
defer wg.Done()

fmt.Printf("Working on %s\n", msg)
time.Sleep(time.Second)
fmt.Printf("Done working on %s\n", msg)

}

func main() {
// Add a count of the total number of goroutines we're going to use.
wg.Add(2)
go worker("Task 1")
go worker("Task 2")

// Wait until the WaitGroup counter is zero i.e., all routines have finished
wg.Wait()
fmt.Println("All tasks completed!")

}

In this code, main doesn’t exit until worker has finished both its tasks.

Remember, the power of Go’s concurrency is best harnessed when coupled with synchronization tools like WaitGroup. In a real-world scenario, you’d be using these to do complex task orchestration.

Let’s move on to our next topic.

Topic: Rate Limiting

Rate limiting is a way to control how many requests are made to a server in a given amount of time. It’s important for maintaining the quality of service and keeping servers from being overwhelmed.

Here’s an example of how to implement rate limiting in Go:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func main() {  
requests := make(chan int, 5)

for i := 1; i <= 5; i++ {
requests <- i
}
close(requests)

limiter := time.Tick(200 * time.Millisecond)

for req := range requests {
<-limiter
fmt.Println("request", req, time.Now())
}

}

In this sample code, we’re running a loop sending 5 requests and storing them in a channel. Then, we set up a rate limiter that will tick every 200 milliseconds.

For each request, we receive a tick from the limiter before we print the request. This ensures that we’re not handling more than one request every 200 milliseconds.

Topic: Case Studies: Building a Job Scheduler and a Rate-limited Service

Case Study 1: Building a Job Scheduler

Job scheduling is a common task in software engineering where certain tasks need to be executed at a specific interval or at a specified time in the future.

Here’s some simple code that schedules a function to run every 2 seconds:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
"fmt"
"time"
)

func printTime() {
fmt.Println(time.Now())
}

func main() {
tick := time.Tick(2 * time.Second)
go func() {
for range tick {
printTime()
}
}()
// Hold main from exiting
time.Sleep(10 * time.Second)
}

In this Go code, we have scheduled the printTime function to execute every 2 seconds by using the built-in time.Tick function.

Case Study 2: Building a Rate-limited Service

Building upon the rate-limiter we discussed earlier, let’s consider an HTTP server that uses this rate limiter to handle incoming requests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package main  

import (
"fmt"
"net/http"
"time"
)

var limiter = time.Tick(200 * time.Millisecond)

func rateLimitedHandler(w http.ResponseWriter, r *http.Request) {
<-limiter // Block until we receive from the limiter channel (i.e., "rate limit" ourselves)
fmt.Fprint(w, "Web page content")
}

func main() {
http.HandleFunc("/", rateLimitedHandler)
http.ListenAndServe(":8080", nil)
}

This would be a simplified sketch of a rate-limited HTTP server.

It’s crucial to note that the complexities of building real-world schedulers and rate-limited services go far and beyond this simple introduction. But with a strong grasp of these concurrency principles and mechanisms, you’re well on your way to building robust systems in Go.

Topic: Review and Assessment

Review:

We have covered a myriad of important “Advanced Concurrency” concepts:

  1. Timers: Useful for when you want to do something once in the future.
  2. Tickers: Useful when you want to do something repeatedly at regular intervals.
  3. Worker Pools: Implementing worker pools can be a good way to limit resource usage. They are used where we have a large number of goroutines to do similar kind of tasks.
  4. Waitgroups: Useful for waiting for a collection of goroutines to finish executing.
  5. Rate Limiting: Important to ensure our programs don’t overwhelm the resources they use too much. Also used to control the rate of actions that can be done; for instance, control the rate of incoming requests.

Done in a typically “Go” way, we used channels and goroutines to tackle these concurrency concepts.

Assessment:

If you now understand these advanced concepts of concurrency then it’s time for a quiz. Let’s see if you’re ready to work on complex problems that require the application of these skills.

  1. Timer Practice Problem:
    Write a program that prints a message “Timer expired” after 3 seconds using a timer.
  2. Tickets Practice Problem:
    Write a function that ticks every 100ms and stops after 1 second.
  3. Worker Pools Practice Problem:
    Create a worker pool of size 3. Submit jobs to print the squares of numbers. The job should print the square of inputs after a delay.
  4. Waitgroups Practice Problem:
    Write a program to sync the execution of two goroutines using WaitGroup.
  5. Rate Limiting Problem:
    Write a program that allows only 3 requests to be handled every second.

These problems should give you a practical understanding of how these advanced concurrency concepts play out. If you could come up with code for these, congratulations! If not, or if you have doubts, simply ask me and I’ll help you out.

Remember, the best way to learn Go or any programming language is by constant practice. Keep exploring, keep implementing. Happy coding! 🦌

Once you’ve tried solving these problems, let me know and we can review the answers together!

中文文章: https://programmerscareer.com/zh-cn/go-basic-07/
Author: Wesley Wei – Twitter Wesley Wei – Medium
Note: If you choose to repost or use this article, please cite the original source.

Go Basic: Understanding Reading Files, Writing Files, Line Filters in Go Go Basic: Understanding Atomic, Mutex, Stateful Goroutines in Go

Comments

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×