4 ms·
I started reading this article strongly disagreeing with it, but am convinced to the point that I would really like to try this in practice. My initial disagre
by Laremere 6y ago
I started reading this article strongly disagreeing with it, but am convinced to the point that I would really like to try this in practice.
My initial disagreement was centered in that nurseries seemed overly optimized for the "do a bunch of processing in parallel and then return a result" use case. This seems nice, but not necessary as Go's channel and waitgroup privatives solve this easily enough.
My concerns were around use cases that I can initially think of, which don't fit this easy mold:
- Running servers, with connections handled on threads (eg, typical Go http.ListenAndServe). However this works with nurseries very well (when it doesn't with, eg, parallel do). I expect that if I have that server run, it will only return with an error or having been stopped, and that all the pending connections will have been finalized.
- Game engines: If I want to write a concurrent game engine, I don't want to be constantly creating and removing threads. Instead I want to spawn several well organized threads, some with their own tasks, others working from a shared pool. I want this processes to exist for the entirety of the time the game is running, so do I really need nurseries to manage cleanup and reuniting processes? That characteristic doesn't solve any significant problems. However, the statement that if you want to allow a function to run something in the background, you need to pass it an already opened nursery is convincing. This structures the main function to likely have a nursery created for the engine game engine process, and passed to the various systems which need to start background threads. This should make the important reasoning about carefully scheduling work clearer to look at and trace.
- I recently worked on a project where we had a large volume of queries on fast changing data, with reasonably limited total data requirements. The solution was to have a background thread do the following:
1. Collect all waiting requests.
2. Update the internal cache with the state of the entire system.
3. In parallel let the various requests run their queries on the cached state.
4. Wait for the queries to finish, before restarting with #1.
This enforces that requests always get state which is ordered after the request started (so create then query won't have a race condition), and is easy to reason about as the state of the cache cleanly alternates between being written by one thread, and read by many threads.
Nurseries really don't help at all with this use case. However they don't harm it in any way either: The request threads are started by the server handling requests, which is blocking the main thread. The cache handling thread is started by the main thread before starting the server, which just works. The main thread keeps the nursery around, showing that if you looked at the main thread that a background task would be running there.
So, all in all, I look forward to trying this style of concurrency.