During the development of Spiteful Hangover I made a CPU-bound particle system.
Meaning the particles lived and updated on the CPU. This allowed us more control over the particles as well as making it easier to add sprite and model particles. As well as making it so we can use different vertex and pixel shaders if we need to.
Since particle system normally update and handle many particles I had to make sure that it would not destroy our frame rate in the game. So early in the development of the particle system I added a the BS Thread Pool library to the project. Where I assigned every emitter which held their respective particle buffer as their own job, so the Thread Pool update the emitters while the game was running other systems. So we only had to wait at the rendering stage of the particles system.
Since each emitter only updates their own particles there's not much needed to do for thread safety aside from when we're expanding the particle arrays or manipulating data in the emitters. While at the same time we can keep emitters alive until all particles are dead, the array is empty, and we can easily skip updating empty emitters.
Having setup how we store and update particles only rendering was left. I started by rendering the particles per emitter since the model and shaders was saved in a per emitter basis, which worked fine in the beginning. But since in a game you usually have many of the same type of emitters for example blood splatter, it was not to efficient since we got a seperate draw call per emitter. While it was not a big problem, the solution was not to hard to do either. We simply made it so that at creation we stored a key for the emitters, which we based of the model or sprite that the emitter used as well as their shader.