TL DR
If you really need to get as much as possible from your employees, just use the event semaphore, control unit and barrier instead of your semaphores. Please note, however, that this is a more fragile solution, so you need to balance any potential benefits from this drawback.
Context
First I need to summarize the wider context in our discussion ...
You have a graphical Windows application. It has the desired frame rate, so you need the main thread to execute at such a speed, scheduling all your employees exactly at a time interval, so that they complete their work within the update interval. This means that you have very tight restrictions on startup and execution times for each thread. In addition, your workflows are not all the same, so you cannot just use one work queue.
Problem
Like any modern operating system, Windows has many synchronization primitives . However, none of them directly provides a mechanism for notifying several primitives at once. Looking through other operating systems, I see a similar model; they all provide wait methods for several primitives, but none of them provide an atomic way to run .
So what can we do instead? Problems you need to solve:
- Precise timing for all required workers.
- Pushing workers who really need to run in the next frame.
Functions
The most obvious solution to problem 1 is to use only one event semaphore, but you can also use read / write locks (by acquiring write locks after workers have finished work and forcing workers to use read locks), all other parameters are no longer atomic, therefore, additional synchronization is required to force the threads to do what you want - for example, the loss proposal for locks within semaphores.
But we need an optimal solution that minimizes context switches due to limited time constraints for your application, so let's see if any of them can be used to solve problem 2 ... How can you choose which workflows should be started from main, if we only have an event semaphore or a read / write lock?
Well ... Read / write locks are a great way for one thread to write some important data to the control unit and for many others to read from it. Why not just have a simple array of boolean flags (one for each worker thread) so that your main thread updates every frame? Unfortunately, you still need to stop running workers until a timer appears. In short, we again return to solving semaphore and blocking.
However, due to the nature of your application, you can take another step. You can rely on the fact that you know that your employees do not work outside of your time and use the event semaphore as a crude form of blocking.
The final optimization (if your environment supports them) is to use a barrier instead of the main semaphore. You know that all n threads must be idle before you can continue, so just insist on it.
Decision
Applying this above, your pseudo-code will look something like this:
def main_thread(n): main_event = event() for i = 1 to n: worker_scheduled[i] = False spawn_thread(worker_thread, i) main_barrier = barrier(n+1) while True: ...do some work... workers_to_wake = foo() for i in workers_to_wake: worker_scheduled[i] = True main_event.set() main_barrier.enter()
Since there is no explicit protection for the worker_scheduled array, this is a much more fragile solution.
Therefore, I would personally use it only if I had to squeeze every last ounce of processing from my processor, but it seems that this is exactly what you are looking for.