When doing CPU intensive tasks like image resizing it doesn’t make sense to create more goroutines than available CPUs.

Things will not go any faster and you might even loose performance due to additional book keeping and switching between goroutines.

One way to limit concurrency (i.e. number of gorutines you launch at the same time) is to use a semaphore.

You can enter (acquire) semaphore and leave (release) a semaphore.

A semaphore has a fixed capacity. If you exceed semaphore’s capacity, acquire blocks until a release operation frees it.

A buffered channel is a natural semaphore.

Here’s an example of using a channel acting as a semaphore to limit number of goroutines active at any given time:

https://codeeval.dev/gist/7a156c60e212611cc366000fe4938c1e

We use technique described in waiting for goroutines to finish to wait for all tasks to finish.

Often the right amount of concurrent tasks is equal to number of CPUs, which can be obtained with [runtime.NumCPU()](<https://golang.org/pkg/runtime/#NumCPU>).