This is a generic question that I’ve always wondered.
In general, is it intensive for a CPU to create threads that perform “while not true …” loops or similar?
For example, suppose I do:
// pseudo-code
new Thread(function() {
while (!useHasDoneSomething) {
// do nothing...
}
alert("you did something!");
}
So it’s just a piece of code that runs in a thread that waits for something to happen.
For whatever reason I imagine that this would pressure the CPU. After all, even though it’s just one little loop, it’s re-running the loop the instant that a cycle is done. Wouldn’t this mean that it’s going through the cycle many times per millisecond? … Per nanosecond!?
The operating system will “handle” the program and the thread(s), right? But that’s still an awful lot of attention to running a loop. If the CPU has nothing else to do, wouldn’t it just focus on that loop, thereby consuming all the idle time?
What if I create 1000 threads that all do the same loop? How might that affect performance/CPU cycles?
Furthermore, is this how events function internally? When you’re ‘listening’ for an event in Java, .NET, Javascript etc (such as a button press or a window resize), has a thread that loops been created?
3
In general, is it intensive for a CPU to create threads that perform “while not true …” loops or similar?
Yes. Using such a loop is called busy waiting and it is called that for a reason. These busy loops check the condition as often and as fast as the processor can manage it, with the result that, if you stay in the loop long enough, the processor load goes reliably to 100%.
What if I create 1000 threads that all do the same loop? How might that affect performance/CPU cycles?
It only creates more work for the CPU doing noting.
Furthermore, is this how events function internally? When you’re ‘listening’ for an event in Java, .NET, Javascript etc (such as a button press or a window resize), has a thread that loops been created?
No. Busy loops are a form of polling and a very inefficient one at that as well. Operating systems have other mechanisms to detect that an event has happened (e.g. an interrupt or a specific call) and they have mechanisms to let a thread wait for it without consuming CPU time.
These mechanisms will also be used to implement the event mechanisms of languages like Java.
2
Your instinct is correct. Busy-waiting like that is described as
Spinning can be a valid strategy in certain circumstances, most notably in the implementation of spinlocks within operating systems designed to run on SMP systems. In general, however, spinning is considered an anti-pattern and should be avoided, as processor time that could be used to execute a different task is instead wasted on useless activity.
Instead, you should consider a concurrency control primitive that allows the thread to truly go idle until it has useful work to do, for example a semaphore or a condition variable:
For many applications, mutual exclusion is not enough. Threads attempting an operation may need to wait until some condition holds true. A busy waiting loop, like
while not( condition ) do // nothing end
will not work, as mutual exclusion will prevent any other thread from entering the monitor to make the condition true. Other “solutions” exist. Such as having a loop that unlocks the monitor, waits a certain amount, locks the monitor and check for the condition P. Theoretically, it works and will not deadlock, but issues arise. It’s hard to decide an appropiate amount of waiting time, too small and the thread will hog the CPU, too big and it will be apparently unresponsive. What is needed is a way to signal the thread when the condition P is true (or could be true).
The solution is condition variables. Conceptually a condition variable is a queue of threads, associated with a monitor, on which a thread may wait for some condition to become true. Thus each condition variable is associated with an assertion . While a thread is waiting on a condition variable, that thread is not considered to occupy the monitor, and so other threads may enter the monitor to change the monitor’s state. In most types of monitors, these other threads may signal the condition variable to indicate that assertion is true in the current state.
4
Threads should wait for incoming events, not for variable changes.
Incoming events are semaphores becoming available, message queues becoming not empty or time elapsed:
semGet()
msqRead()
sleep()
From the thread point of view, these function calls are blocking: the thread waits until the event has come, leaving CPU load to other threads.
From the system point of view, the thread is marked as ‘Waiting for event’ and will not be scheduled as long as the event has not come.
About hardware events, they are handled through interrupts which are electric signals to the CPU and which trigger the execution of ISR (interrupt service routines), whose role is to produce a software event on semaphores, message queues or time.
The //do nothing
part inside while condition in normal scenario is WAIT on some semaphore, or in other terms SLEEP. Sleep until some interrupt has woken you up. And hence the thread goes in “sleep state” or lets just say “not running state”. Only the processes which are in running state consumes CPU. The sleep state process will not consume CPU. They may be consuming memory, but that memory will be swapped out internally by virtual memory system. So even if you create 1000 such threads they will not be posing much threat to your system.