21

I opened up Task Manager and looked under the "System" area and saw:

Threads: 1337

Since I have a dual-core processor with hyper-threading available (meaning four threads), how is it possible to have 1000+ threads when my processor is only supposed to have four?

4
  • 3
    Isn't that why they call it hyper-threading? :)
    – user
    Jul 5, 2013 at 8:01
  • 9
    Gee, have they finally invented "multiprogramming"??? (This is 1967, right?) Jul 5, 2013 at 11:13
  • 10
    Did someone just change the number to be 1337? Jul 5, 2013 at 16:05
  • 11
    How can a company with four desks have 1337 employees? Easy; the employees take turns using the desks. Jul 5, 2013 at 16:54

3 Answers 3

56

The simple answer is that not all threads are executing simultaneously. For a fuller explanation, read on.

The operating system's task scheduler is generally thought of to schedule applications, and doing so allows you to perform one task while the computer is working on another. In the old days, the litmus test of multitasking was formatting a floppy disk while doing something else. If you wanted to really put the OS to the test, you'd format a floppy disk while downloading a file over a modem connected to the serial port. As the hardware became powerful enough to actually do it in a meaningful way, video playback sometimes featured in such tests as well. If the OS's task scheduler could handle running those tasks smoothly, then it could handle anything.

However, the task scheduler doesn't actually schedule applications (processes), it schedules threads. Each application has at least one thread, but can potentially use a large number of threads to split the work it does into related or independent parts. For example, it is common for an application to have one thread that handles the user interface, and to create another thread when the user initiates a potentially long-running operation (which might be things like printing, recalculating a spreadsheet, a development environment doing a symbol lookup, etc. etc.). Some programming environments introduce some amount of threads invisibly-to-the-programmer; for example, Java and .NET might do garbage collection in a separate thread, which is out of the immediate control of the programmer. Some programs create a number of threads early on and pool them, because creating new threads is a comparatively expensive operation (so you don't necessarily want to have to create a thread every time you need one). Anything that does a preview is usually done in a separate thread, so the remainder of the UI remains responsive while the preview is being generated. And so on. Taken together, all this means that the number of threads on the system at any time can easily be many times the number of processes.

Each thread can be in one of a few possible states, but the most important distinction is between running, runnable and waiting states; the terminology can differ slightly, but that's the general idea. At any one time, only one thread per virtual (because of hyperthreading and similar technologies) CPU core can be running (that is, executing machine code instructions), but any number of threads can be runnable (meaning that it is a candidate to get the CPU the next time the scheduler needs to make a decision about which thread should be allowed to run). Waiting (also known as blocked) threads are just that, waiting for something - the most common cases probably are that it is waiting for user, disk or network I/O (user input in particular is exceptionally slow).

The thread count you see in the task manager is the total number of threads in any of these states. For example, the Windows 7 system I am typing this on currently has around 70 processes started but almost 900 threads. With all the background processes to handle various tasks and how they are probably subdivided into a multitude of threads each, this is not an outrageous number.

Going a bit more into the depths of technical implementation, at the very core of a pre-emptively multitasking operating system's task scheduler is usually some kind of hardware interrupt hook. This means that the kernel can halt the CPU when it has no useful work to perform (this is almost certainly one of the reasons, if not the reason, why Linux checks the HLT instruction on boot on IA-32-compatible CPUs, and probably does similar checks on other architectures), safe in the knowledge that at some reasonably determinate future time, an interrupt will fire and the task scheduler will be invoked. Since the interrupt fires regardless of what other work the CPU is performing (that's the idea behind interrupts), the scheduler gets executed regularly and gets a chance to determine which thread should be executed during the following time slice. Since context switches are relatively expensive it's usually possible (at least through the source code) to tune how aggressively the scheduler switches between threads; switching threads more often causes the system to become more responsive, but the switching overhead means that the overall time to finish a given set of tasks is longer. The fastest system will be one that only switches between threads when the running thread is no longer possible to run (meaning it gets blocked waiting on something, or it has finished its job) because that minimizes the overhead, whereas the most responsive system will switch between threads every time the scheduler is invoked because that minimizes the average time to wait before a particular thread gets CPU time. The ideal setting usually is somewhere in between these two, and the tradeoff between those choices is likely one big reason why Linux offers multiple schedulers to choose from as well as some tuning parameters through the kernel configuration.

Cooperatively multitasking OSes and environments, on the other hand (Windows 3.x being one example), rely on each application to regularly cede control to the scheduler. There's usually an API function specifically meant to do that, and oftentimes many API functions will do it as part of their internal execution flow, because it helps make the user experience smoother. That design approach works well as long as all applications are well-behaved and cede control with short intervals during any long-running operations (long-running meaning more than a small fraction of a second), but an application that does not can clog up the entire system. This is one big reason why Windows 3.x did so poorly on the multitasking test I mentioned above, while OS/2 strolled along merrily while performing the same tasks on the same hardware: an application could tell the floppy disk drive to write a certain sector, and the time it took to do so before the call returned could actually be measurable (tens to hundreds of milliseconds or more); a preemptively multitasking system would have its scheduler break in on its next scheduled invocation, notice that the thread that is currently "running" is actually blocked by the write call and simply switch to another thread that is runnable. (In practice it's a little more involved, but that's the general idea.)

In both preemptively multitasking and cooperative environments, there is also the possibility of different threads having different priorities. For example, it is probably more important to execute in a timely fashion the thread that is receiving data over a communications link than the one that updates the system time display, so the receiving thread has high priority and the time display updater thread has low priority. Thread priorities play a role in the scheduler's decision of which thread to allow to execute (for example, very simplified, high priority threads should always execute before low priority threads, so even if the low priority thread has work left to do, if the high priority thread becomes runnable it takes precedence), but such specific scheduling decisions do not affect the underlying mechanism design.

20
  • 11
    I love "user input in particular is exceptionally slow" Jul 5, 2013 at 8:56
  • The "threads" a CPU "has" refers to the number it can execute simultaneously at any given moment. It switches between active threads, giving each a turn, or share of the CPU for a slice of time, when possible. Processes/threads "blocked" or waiting on I/O (such as disk, keyboard/mouse input, etc.) or something else (such as synchronization primitives like mutexes, etc.) skip their turn.
    – LawrenceC
    Jul 5, 2013 at 14:48
  • 1
    Comments are nice to ask for clarification, but they're not suited for extended discussion, and at some point become hard to read through. Can you please take this to Super User Chat instead? Thank you.
    – slhck
    Jul 6, 2013 at 14:45
  • 1
    @slhck I created a room, but couldn't find any way to migrate the discussion in these comments, which would be nice. Is that something you can do manually as a moderator? chat.stackexchange.com/rooms/9547/…
    – user
    Jul 6, 2013 at 20:12
  • 1
    Unfortunately no. There's an automatic migration process that cannot be triggered manually, but we can't move comments to a chat room. We'll let the comments here stay for the time being, but I'd encourage others to follow up on the discussion in the chat room you created.
    – slhck
    Jul 6, 2013 at 20:20
18

Think about a four laned highway with 1037 vehicles.

Your OS need a lot of running processes to work for lots of services. Even the most simple graphical programs will require multithreaded programming. When you think of your lot of programs opened you see there is a need for sharing the computing power resources.

What your task manager is showing is the current system load. What your comp specs is showing is how many threads are (in frontend) being accepted for parallel execution. Without entering muchly in the difference between hyperthreading and multicore features, with more logical frontend thread accepting, a system will generally perform better.

6
  • 8
    "Even the most simple graphical programs will require multithreaded programming." Wrong. It is perfectly possible to write single-threaded GUI applications; up until Windows 95, for all intents and purposes everyone did it that way. It makes certain tasks more complicated (for example, background printing is trivial with multiple threads but decidedly non-trivial in a single threaded application, particularly if you are also memory constrained as was the case then), but there is a huge difference between "X is made easier by Y" and "X requires Y".
    – user
    Jul 5, 2013 at 8:59
  • 8
    @MichaelKjörling: "up until Windows 95, for all intents and purposes everyone did it that way"* - really? Even on *nix systems running Motif in the 80's?
    – LarsH
    Jul 5, 2013 at 14:04
  • @LarsH Good point, and one I thought of too late to edit into the comment. But that does not negate the point: namely, that it is perfectly possible to write single-threaded GUI applications. You don't need multithreading for that, although it does make some tasks (considerably) easier on the programmer.
    – user
    Jul 5, 2013 at 15:51
  • @MichaelKjörling: I agree, it doesn't negate your point, which is a valid one. (I don't think uprego's incorrect statement negates his point either.)
    – LarsH
    Jul 5, 2013 at 18:14
  • As an example to what @MichaelKjörling said, Visual Basic (before .NET) as a programming language had no multi-threading support. Everything was run on a single thread. If you wanted to process user input in the middle of a long-running operation, you'd call DoEvents, which would process the message queue - but that was done on the same thread and would block that long-running operation until all messages were processed. (Of course, you could call Win32 API functions and/or create additional processes, but at that point you may as well use one of the lower-level languages.)
    – Bob
    Jul 6, 2013 at 12:10
5

We should step back and ask ourselves: How can a computer with a single CPU have two threads?

Threads are software entities, not hardware. To have another thread, you just need memory for the objects that make up the thread, such as a descriptor structure and a stack.

The operating system switches among threads at various times, like inside certain interrupts (such as a timer interrupt) or when threads make calls into the operating system.

Out of all of the threads which exist in the system, only a subset are usually in a state which is commonly called "runnable". Runnable threads are eager to run: they are either executing, or sitting in a "run queue", waiting to be dispatched by the scheduler. Threads which are not runnable are "blocked", waiting to acquire some resource or receive input, or "sleeping" which is like being blocked on input, where the "input" is the passage of time. A "context switch" takes place when the scheduler function in the operating system takes a look at the run queue of a processor, and chooses a different thread to execute.

Do not be confused by "hyperthreading", which is Intel's name for a particular hardware feature.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .