Multitasking is implemented by the operating system scheduler which determines which of the competing threads will receive the next slice of the processor's time. Each timeslice consists of a fixed number of clock ticks. The Win32 scheduler is preemptive, gaining control whenever a clock interrupt occurs, at which time it decrements the count of clock ticks remaining in the timeslice of the currently executing thread. If the count goes to zero , a scheduling decision is made to determine which thread should execute next. The next timeslice always goes to the thread that has the highest priority of those that are ready to execute. When the highest priority is shared by more than one executable thread, the timeslice goes to the one that has been waiting longest. Threads that are suspended or blocked (for example, waiting for a semaphore object or waiting for input) are not executable, and so until they become unblocked, the scheduler does not allocate any processor time to them regardless of their priority. When the scheduler decides to award the next timeslice to a different thread, the context of the currently executing thread is saved, and the machine state is set according to the context of the thread to be executed.
Thread priorities are controlled at both the process level and the thread level. Every process is in one of three priority classes: high, normal, or idle. By default, the priority class of a process is set to normal; but an application can change the priority class of an executing process. And a parent process can specify a priority class when it creates a child process. There are five priority levels within each priority class, ranging from two points above to two points below normal (-2..2). The base priority of each thread is relative to the priority class of its process. All threads are started with normal priority, but this can be changed to any of the five levels within the class of its process. If the priority class of a process is changed, the relative priorities of its threads remains unchanged.
In addition to a base priority, all threads have a dynamic priority which is never less than the base priority. It is actually the dynamic priorities that are used to make scheduling decisions. The scheduler raises and lowers the dynamic priority of a thread to enhance its responsiveness when significant things happen to the thread. The following events cause dynamic boosts:
o Whenever a window receives input (such as timer messages, mouse move messages, keyboard input), a boost is given to all threads within the process that owns the window.
o In some cases, the dynamic priority of a waiting thread is raised when the wait is satisfied. For example, a wait associated with disk or keyboard I/O results in a dynamic boost when the input occurs.
When the dynamic priority of a thread has been raised, it is reduced by one level each time the thread completes a timeslice until it has dropped back to its base priority. In addition to these dynamic priority boosts, the system raises the base priority class of the foreground application so it is greater than or equal to the priority class of any background applications. The application's priority class returns to its original setting when it is no longer in the foreground.
On a multiprocessor system, the the highest N runnable threads are executed (where N is the number of processors).