I recently saw a very hotly debated article, “Concurrency is still not easy”, which even made it to Hack News. If you are interested, you can take a look at it, and this article will use a real-life example to introduce why the author says that writing Concurrency is not so easy. As you know, in Go Language, you can write Concurrency easily by using the keyword go
, but multiple Goroutines need to communicate by channel. There are a bunch of Concurrency Patterns available on the web for developers, but the official Go standard library doesn’t include them, so it’s hard to see the problem after implementation. The article mentions that gops encountered the problem of the whole system hanging when implementing Limit Concurrency? What is Limit Concurrency? It is when the system has multiple jobs that need to be executed at the same time, but it needs to limit the amount of Concurrency to avoid the whole resource being eaten up. The following is a description of the problem encountered in the article.
Limit Concurrency problem
Gops is a set of CLI tools that lists all the processes running in Go on the system. Simply install the gops command and run gops
first to see the results below.
Then use the following code to continue to fill up the process to 10, and then run gops
to find that the system does not show up at all, and it does not end.
This problem occurred on 8/5 when a user proposed PR to restrict Concurrency, which caused the above problem, causing the CLI to fail to continue to run and require ctrl + c
to end the execution. At the bottom is the modified code:
|
|
I simplified the above example to a single main function and it works the same way:
|
|
I replaced the ps.Processes()
data with the Job count to explain why this code caused the system to hang. The main reason is the limitCh <- struct{}{}
in the for loop, which is set to only run 10 Concurrency Processes at a time.
This is a standard Limit Concurrency problem, after the first Job is read, the empty struct is first dropped into the limitCh channel, at this time limitCh is the remaining 9 can continue to process, and then continue the same action. But when the 11th Job needs to be processed, it will directly stop at limitCh <- struct{}{}
, and the code behind the for loop will not be able to execute at all, causing the whole system to deadlock.
From this, we can see that if the number of Processes is less than 10, we can hardly see any problem and the system will work normally (you can replace the Job Count of the example with 10). The following will introduce two ways to bypass this problem, you can refer to see.
Throw limitCh <- struct{}{} into the background
I believe many developers may think that since they are stuck in limitCh <- struct{}{}
, they can just throw this code into the goroutine as well.
|
|
I’m glad to see that this approach solves the system hang problem. But have you noticed that the code does not limit Concurrency Processes, but rather 100 Jobs at the same time to the end? Although this approach can solve the problem, but back to the original purpose of the problem, we are to write Limit Concurrency ah.
Writing with worker queue
This is quite common. Since we need to limit the number of workers that can be processed at the same time in the background, it is relative to create a specific number of workers, and each worker reads the data out of Channle. The first step is to create a queue channel and drop all the content into the queue.
This side is also dropped into the background by goroutine to avoid blocking the whole main program. Then a specific worker is created to handle all the jobs.
|
|
As you can see, the for loop here is dominated by concurrencyProcesses
, and the goroutine is used to read the channels until all the channels are read, and the whole goroutine will be finished. In addition to this solution, there are still other ways to implement it, so we’ll leave it to you to play.
Summary
The reason why this article is so popular, I guess it’s also because the Repo is officially developed by Google itself, but all the PRs need to go through Googler’s strict review before they can be merged into the Master branch, but like this small detail, if not really tested, it’s really quite difficult to find problems.