1. Introduction

Modern CPUs are made up of more than one core. So, in order to leverage the power of multi-core processors, applications generally run on multi-threaded processes. Sometimes, we may need information on how many threads a particular process uses. In this article, we will walk you through all the tools and commands we can utilize to check and monitor the thread count data.

We assume that the concepts of thread and process are known. Hence, if you’re not familiar with or want to learn more about the difference between threads and processes, make sure to read our article, Linux Process vs. Thread.

2. Checking the Thread Count

In this section, we’ll see how we can get the immediate thread count information of a process. Altogether, we have several options for that. Let’s dive right in.

2.1. Using the ps Command

The command ps displays information about current active processes on the computer. Combining the options -e and -f is a standard approach we can use to get the information on every process in a formatted way. Similarly, we can attach the -L option to make ps report the additional thread information:

$ ps -eLf
UID          PID    PPID     LWP  C NLWP STIME TTY          TIME CMD
root           1       0       1  0    1 12:00 ?        00:00:01 /sbin/init splash
root           2       0       2  0    1 12:00 ?        00:00:00 [kthreadd]
root           3       2       3  0    1 12:00 ?        00:00:00 [rcu_gp]
root           4       2       4  0    1 12:00 ?        00:00:00 [rcu_par_gp]
root           6       2       6  0    1 12:00 ?        00:00:00 [kworker/0:0H-events_highpri]
root           9       2       9  0    1 12:00 ?        00:00:00 [mm_percpu_wq]
root          10       2      10  0    1 12:00 ?        00:00:00 [rcu_tasks_rude_]
root          11       2      11  0    1 12:00 ?        00:00:00 [rcu_tasks_trace]
root          12       2      12  0    1 12:00 ?        00:00:00 [ksoftirqd/0]
root          13       2      13  0    1 12:00 ?        00:00:00 [rcu_sched]
root          14       2      14  0    1 12:00 ?        00:00:00 [migration/0]
root          15       2      15  0    1 12:00 ?        00:00:00 [idle_inject/0]
root          16       2      16  0    1 12:00 ?        00:00:00 [cpuhp/0]
root          17       2      17  0    1 12:00 ?        00:00:00 [cpuhp/1]
.
.
.
baeldung    1970    1350    1970  0    5 12:01 ?        00:00:01 /usr/libexec/gnome-terminal-server
baeldung    1970    1350    1971  0    5 12:01 ?        00:00:00 /usr/libexec/gnome-terminal-server
baeldung    1970    1350    1972  0    5 12:01 ?        00:00:00 /usr/libexec/gnome-terminal-server
baeldung    1970    1350    1973  0    5 12:01 ?        00:00:00 /usr/libexec/gnome-terminal-server
baeldung    1970    1350    1977  0    5 12:01 ?        00:00:00 /usr/libexec/gnome-terminal-server
baeldung    1978    1970    1978  0    1 12:01 pts/0    00:00:00 bash
baeldung    1996    1350    1996  0    3 12:02 ?        00:00:00 /usr/libexec/gvfsd-metadata
baeldung    1996    1350    1997  0    3 12:02 ?        00:00:00 /usr/libexec/gvfsd-metadata
baeldung    1996    1350    1998  0    3 12:02 ?        00:00:00 /usr/libexec/gvfsd-metadata
baeldung    1999    1607    1999  0    4 12:02 ?        00:00:00 update-notifier
baeldung    1999    1607    2002  0    4 12:02 ?        00:00:00 update-notifier
baeldung    1999    1607    2003  0    4 12:02 ?        00:00:00 update-notifier
baeldung    1999    1607    2004  0    4 12:02 ?        00:00:00 update-notifier
root        2139       2    2139  0    1 12:07 ?        00:00:00 [kworker/u4:1-events_unbound]
root        2149       2    2149  0    1 12:15 ?        00:00:00 [kworker/1:1-cgroup_destroy]
root        2240       2    2240  0    1 12:32 ?        00:00:00 [kworker/0:1-events]
root        2241       2    2241  0    1 12:32 ?        00:00:00 [kworker/0:3-inet_frag_wq]
root        2247       2    2247  0    1 12:36 ?        00:00:00 [kworker/u4:0-events_unbound]
root        2298       2    2298  0    1 12:43 ?        00:00:00 [kworker/u4:2]
root        2335       2    2335  0    1 12:44 ?        00:00:00 [kworker/1:0-cgroup_destroy]
root        2336       2    2336  0    1 12:44 ?        00:00:00 [kworker/0:0-events]
baeldung    2346    1978    2346  0    1 12:45 pts/0    00:00:00 ps -eLf

In the output, the NLWP (Number of Light Weight Processes) column indicates the number of threads attached to that particular process. For example, when we take a look at a process that has an NLWP value of more than one, we can see that the corresponding number of rows after it has the same PID value.

However, if we don’t want to receive all this messy information for every process and we’re only searching for the number of threads of a specific process, we can make use of ps with the -o option:

$ ps -o nlwp 1970
NLWP
   5

Here, nlwp indicates the column we used to get the thread count information above, and 1970 is the PID of a process. As we can see, this process runs five threads simultaneously. We can get the same result by using thcount instead of nlwp if we’d like:

$ ps -o thcount 1970
THCNT
    5

2.2. Inspecting the /proc Directory

In a similar fashion, we can examine the /proc directory for thread count data. /proc is a virtual file system that contains various information about processes. Let’s take a look at what information we can get:

$ ls /proc/1970/task/
1970  1971  1972  1973  1977

As we see clearly above, the process with PID 1970 contains five threads. In fact, this result matches what we got with the command ps previously. Furthermore, we can verify that these directory names are exactly the same as the appropriate results under the LWP column.

Another way to look for thread count information is by examining the status file under the convenient folder:

$ cat /proc/1970/status | grep Threads
Threads:    5

3. Monitoring the Thread Count

So far, we’ve looked at getting the immediate result by using several methods. Let’s now see the case when we want to monitor the status in real-time.

3.1. Combining Previous Commands With watch

When we use the tool watch combined with the commands we mentioned in the previous section, we can monitor the thread status of any process in real-time. We can also specify the update intervals in seconds with the -n option. Let’s see an example:

$ watch -n 1 ps -o thcount 1970
Every 1,0s: ps -o thcount 1970                                          baeldung: Tue Mar 29 16:35:30 2022

THCNT
    5

Please note that the above result refreshes every second. watch runs the command ps -o thcount 1970 every second and shows the output back to the console.

3.2. Making Use of the top Tool

top is a built-in program that reports system information like CPU usage and memory status. We can use this tool to get thread-count-per-process information, but it’s disabled by default. Therefore, we’ll have to manually tweak the tool. First, we need to get into the utility:

$ top

Then, after pressing the key f, we’ll see a list of metrics that top can display. Let’s select the nTH field accordingly with d and head back to the main interface by pressing q:

top - 15:53:11 up  3:52,  1 user,  load average: 0,14, 0,13, 0,09
Tasks: 198 total,   1 running, 197 sleeping,   0 stopped,   0 zombie
%Cpu(s):  1,7 us,  0,7 sy,  0,0 ni, 97,3 id,  0,0 wa,  0,0 hi,  0,3 si,  0,0 st
MiB Mem :    971,2 total,     90,5 free,    543,3 used,    337,4 buff/cache
MiB Swap:    923,3 total,    718,7 free,    204,6 used.    273,4 avail Mem 

    PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND                         nTH 
   1622 baeldung  20   0 4218272 304356  76152 S   2,3  30,6   2:22.32 gnome-shell                     12 
   1416 baeldung  20   0  286816  58088  24356 S   1,7   5,8   0:39.93 Xorg                            2 
   1970 baeldung  20   0  818528  43312  30420 S   1,0   4,4   0:16.31 gnome-terminal-                 5 
   1547 baeldung  20   0  158108   2068   1984 S   0,3   0,2   0:13.21 VBoxClient                      4 
      1 root      20   0  167660   7924   5496 S   0,0   0,8   0:01.38 systemd                         1 
      2 root      20   0       0      0      0 S   0,0   0,0   0:00.00 kthreadd                        1 
      3 root       0 -20       0      0      0 I   0,0   0,0   0:00.00 rcu_gp                          1 
      4 root       0 -20       0      0      0 I   0,0   0,0   0:00.00 rcu_par_gp                      1 
      6 root       0 -20       0      0      0 I   0,0   0,0   0:00.00 kworker/0:0H-events_highpri     1 
      9 root       0 -20       0      0      0 I   0,0   0,0   0:00.00 mm_percpu_wq                    1 
     10 root      20   0       0      0      0 S   0,0   0,0   0:00.00 rcu_tasks_rude_                 1 
     11 root      20   0       0      0      0 S   0,0   0,0   0:00.00 rcu_tasks_trace                 1 
     12 root      20   0       0      0      0 S   0,0   0,0   0:00.21 ksoftirqd/0                     1 
     13 root      20   0       0      0      0 I   0,0   0,0   0:00.53 rcu_sched                       1 
     14 root      rt   0       0      0      0 S   0,0   0,0   0:00.03 migration/0                     1 
     15 root     -51   0       0      0      0 S   0,0   0,0   0:00.00 idle_inject/0                   1 
     16 root      20   0       0      0      0 S   0,0   0,0   0:00.00 cpuhp/0                         1 
     17 root      20   0       0      0      0 S   0,0   0,0   0:00.00 cpuhp/1                         1 

It’s important to note that the process with PID 1970 has five threads, as we saw with previous methods.

We can also use the more modern version htop. The idea is the same. However, keep in mind that top generally comes built-in whereas we may need to install htop manually.

4. Conclusion

To sum up, we’ve now learned how to get and track the thread count information of a process using several tools and methods. Feel free to read the man pages of the tools and try to implement what we’ve learned.