As we know, a process is a currently running program while a thread is a semi-process or a lightweight process.
In this tutorial, we’ll discuss what a multi-threaded process is. Also, we will learn why there’s a limit on the number of threads per process in Linux and what that limit is.
2. What Is a Multi-Threaded Process?
The provision of multiple threads of execution within the same program in shared memory space is called multithreading.
A multithreaded process enables us to run multiple threads concurrently. The purpose of multithreading is to increase performance.
For optimal performance, Linux has a limit on the number of threads. The threads-max kernel parameter can be set to ensure that the number of threads per process is always less than or equal to that limit. However, other factors like the amount of virtual memory and stack size may indirectly govern the number of threads allocated to a process.
3. Limit on Thread Count per Process
Linux has a setting for maximum threads per process, which specifies the maximum number of simultaneous executions that the process can handle.
Changes to this can throttle the process and minimize latencies for the executions that happen.
Reaching this limit means that the process needs that many threads at peak load. But, as long as it can serve requests in a timely manner, the process is adequately tuned.
However, when the limit is reached, threads queue up, potentially overloading the process. At this point, the process defers creating new threads until the number of active threads drops below the limit.
4. How to Retrieve Maximum Thread Count
The kernel parameter threads-max controls the maximum number of threads.
This parameter is defined in the file /proc/sys/kernel/threads-max.
Let’s view this file using the cat command:
$ cat /proc/sys/kernel/threads-max 63704
Here, the output 63704 indicates that the kernel can execute a maximum of 63,704 threads.
Alternatively, we can use the sysctl command to retrieve the threads-max value:
$ sysctl -a | grep threads-max kernel.threads-max = 63704
kernel.pid_max and vm.max_map_count specify two other limits that will also block the new thread creation at peak load.
The pid_max parameter specifies the value at which PIDs wrap around:
$ cat /proc/sys/kernel/pid_max 131072
The kernel.pid_max value of 131072 above means the kernel can execute a maximum of 131,072 processes simultaneously.
The max_map_count parameter specifies the maximum number of Virtual Memory Areas (VMAs) that a process can own:
$ cat /proc/sys/vm/max_map_count 65530
The vm.max_map_count value of 65530 above is the maximum number of memory map areas a process may have.
The Linux kernel handles both a process and a thread in the same way. So, values limiting the number of processes will indirectly also limit the number of threads.
Therefore, kernel.pid_max must be larger than the total number of simultaneous threads and processes.
Having many threads may consume too much memory for the server to work. vm.max_map_count limits both the virtual memory and the number of threads that require this memory for setting up their own private stack.
On systemd systems, the cgroup pids.max parameter enforces another limit. This is set to 12,288 by default. On certain occasions, this default resource limitation is not sufficient, or perhaps too restrictive.
Alternatively, performing specific adjustments on some of the systemd’s TasksMax settings may prove to be useful. The UserTasksMax parameter in the [Login] section of /etc/systemd/logind.conf overrides the default limit:
$ grep -i "^UserTasksMax" /etc/systemd/logind.conf UserTasksMax=50000
This is exactly how systemd also applies a thread limit for programs run from a login shell.
5. How to Set Maximum Thread Count
There are several ways to set the value for the maximum number of threads per process. These values determine how many threads a process will be allowed.
Let’s temporarily set the threads-max kernel parameter at runtime:
$ echo 120000 > /proc/sys/kernel/threads-max
We can permanently set the kernel.threads-max parameter by adding kernel.threads-max=<value> to the /etc/sysctl.conf file:
$ sysctl -w kernel.threads-max=120000 >> /etc/sysctl.conf
Next, setting the pid_max parameter to 200000 means the kernel can execute a maximum of 200,000 processes simultaneously:
$ echo 200000 > /proc/sys/kernel/pid_max
Similarly, setting the max_map_count parameter to 600000 means a process can own a maximum number of 600,000 Virtual Memory Areas (VMAs):
$ echo 600000 > /proc/sys/vm/max_map_count
On systemd systems, the UserTasksMax specifies the TasksMax setting for all users and determines the thread limit:
$ sed -i "s/^UserTasksMax/#UserTasksMax/" /etc/systemd/system.conf $ echo "UserTasksMax=60000" >> /etc/systemd/system.conf
$ grep -i "UserTasksMax" /etc/systemd/logind.conf #UserTasksMax=50000 UserTasksMax=60000
6. Factors That Affect Maximum Thread Count
Although there are system parameters that set a limit on the number of threads per process, the OS and memory likely become the limiting factors well before that.
The limit on the number of threads a process can have is calculated using the formula:
number of threads = total virtual memory / (stack size*1024*1024)
Thus, the number of threads per process can be increased by increasing total virtual memory. The amount of stack size per thread is more likely to be the limit than anything else. Reducing the per-thread stack size is also a way to increase the total number of threads.
We can check the stack size per thread with ulimit:
$ ulimit -a | grep "stack size" stack size (kbytes, -s) 10240
The value signifies that each of the threads will get this amount of memory (10MB) assigned for its stack. With a 32-bit program and a maximum address space of 4GB, the maximum number of threads will be:
4096MB / 10MB = 409
On a 64-bit processor, we can adjust the stack size per thread with ulimit:
$ ulimit -s 8192
In this article, we got an understanding of what a multi-threaded process in Linux is. We also learned the significance of the maximum number of threads per process count in Linux. Finally, we saw how to retrieve and set maximum threads per process count and the factors that affect it.