Python Thread Priority

Abid Ullah Oct 10, 2023
  1. Threading in Python
  2. Controlling Scheduling Priority of Python Threads
Python Thread Priority

This article aims to explain how we can use scheduling threading in Python to make it a priority. Here, we’ll talk about scheduling a priority thread in Python and using it with a good example program.

With Python threading, we can run different parts of our program simultaneously, making your program’s design easier. We will show you how to use threads to speed up your Python program if you know some Python.

Threading in Python

Threading is the capability to execute multiple instructions simultaneously. As we explore in this article, we can prioritize the threads by adjusting the scheduling.

A global interpreter lock (GIL) is used to implement Python threads, meaning a thread’s priority cannot be controlled. Controlling threads using a priority requires us to create an implicit priority system.

In threading, you can imagine two (or more) processors running on your program simultaneously, each performing an independent task.

That’s very close to being true. Each thread will run on one processor simultaneously, even on different processors.

To run multiple tasks simultaneously, you’ll need a non-standard Python implementation, your code may need to be written in a different language, or you may need to use multiprocessing with additional overhead.

Python’s C implementation does not always support threading, so threading may not speed up all tasks. GIL interactions limit the number of Python threads that can run simultaneously.

Generally, threading is a suitable choice for tasks that spend a lot of time waiting for external events. It’s possible that problems requiring heavy CPU computation but spending little time waiting for external events won’t run as fast as others.

Controlling Scheduling Priority of Python Threads

Ensuring we are working with the correct version of Python is crucial. We are using version 3.10.4 specifically for this article.

It is possible to check the currently installed python version by running the following command in the terminal.

python --version

Importing Useful Libraries

The next step is learning how to make a thread using scheduling priority after understanding what a thread is. Most of the primitives you’ll see in this article are provided by the Python standard library through threading.

In this module, Thread provides a nice interface for interacting with threads, encapsulating them nicely.

Now we move on to the next phase of the program, where we import the libraries required for scheduling the priorities of Python threads.

Example code:

import time
from threading import Thread
from time import sleep

It is possible to start a separate thread by using the Thread instance and telling it to .start().

Example code:

import logging
import threading
import time


def the_thread_function(name):
    logging.info("The thread %s: Beginning now", name)
    time.sleep(2)
    logging.info("The Thread %s: Ends", name)


if __name__ == "__main__":
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
    logging.info("Main    : Now before creating a thread")
    x = threading.Thread(target=the_thread_function, args=(1,))
    logging.info("Main    : Earlier than running threading")
    x.start()
    logging.info("Main    : Now wait for the thread to End")
    # x.join()
    logging.info("Main    : All Finish")

Output:

17:06:51: Main    : Now before creating a thread
17:06:51: Main    : Earlier than running threading
17:06:51: The thread 1: Beginning now
17:06:51: Main    : Now wait for the thread to End
17:06:51: Main    : All Finish
17:06:53: The Thread 1: Ends

In the logging statements, you can see that the main section is creating and starting the thread: You create a thread by passing it an argument list and the function you wish to call.

In this case, the Thread is told to run thread_function() and pass it 1 as an argument.

Working With Many Threads Schedule Priority in Python

Currently, the example code works only with two threads, the main thread and the thread you started with the threading Thread object. You will often want to start many threads and have them work on interesting things.

Next, let’s look at an easier method for doing that. Let’s start by looking at the more difficult way.

Example Code:

import logging
import threading
import time


def thread_function(name):
    logging.info("Thread %s: Beginning", name)
    time.sleep(2)
    logging.info("Thread %s: Ending", name)


if __name__ == "__main__":
    format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=format, level=logging.INFO, datefmt="%H:%M:%S")
    threads = list()
    for index in range(3):
        logging.info("Main    : create and start thread %d.", index)
        x = threading.Thread(target=thread_function, args=(index,))
        threads.append(x)
        x.start()
    for index, thread in enumerate(threads):
        logging.info("Main    : Before joining thread %d.", index)
        thread.join()
        logging.info("Main    : thread %d end", index)

Output:

17:09:01: Main    : create and start thread 2.
17:09:01: Thread 2: Beginning
17:09:01: Main    : Before joining thread 0.
17:09:03: Thread 0: Ending
17:09:03: Main    : thread 0 end
17:09:03: Main    : Before joining thread 1.
17:09:03: Thread 1: Ending
17:09:03: Main    : thread 1 end
17:09:03: Main    : Before joining thread 2.
17:09:03: Thread 2: Ending
17:09:03: Main    : thread 2 end

We start a thread the same way we started a thread earlier, by creating a Thread object and then calling .start(). Keeping a list of Thread objects allows the program to wait for them later using .join().

When you look at the output carefully, you’ll notice that all three threads get started in the order you might expect. In this case, however, they finish in the opposite order!

Multiple runs will produce and output the different orders. Look for the Thread x: Nearing End message to tell you when each Thread will go to the end.

Operating systems determine the order in which threads are run, which is quite difficult to predict. Using threading in algorithms may (and probably will) vary from run to run, so keep that in mind when developing algorithms.

It is a great thing that Python provides several primitives for coordinating threads and coordinating them with one another. We’ll first look at how to make managing a group of threads easier.

Creating Worker Class

This worker class allows us to set the script so that each thread can have its execution time. As in the form of a result, we will make the other threads sleep longer if one thread has a higher priority.

The Worker class is a simple MFC class that helps you with multi-threading. Any object that requires its thread to perform work inherits from the Worker class.

Use the Do-Work function to execute the long process - and then use Start/Stop/Pause on the object to perform it.

Example Code:

import time
from threading import Thread
from time import sleep


class Worker(Thread):
    def __init__(self, pri):
        Thread.__init__(self)
        self.pri = pri

    def run(self):
        for i in range(20):
            sleep(1.0 * self.pri)
            print(" -thread with priority:", self.pri)


w1 = Worker(1.0)
w2 = Worker(0.75)
w3 = Worker(0.5)
start = time.time()
w1.start()
w2.start()
w3.start()
end = time.time()

As part of this step, we have initialized workers. Following that, we have created multiple threads with different priorities.

The worker threads will now be executed as follows:

Output exceeds the size limit. Open the full output data in a text editor
 -thread with priority: 0.5
 -thread with priority: 0.75
 -thread with priority: 1.0
 -thread with priority: 0.5
 -thread with priority: 0.75
 -thread with priority: 0.5
 -thread with priority: 1.0
 -thread with priority: 0.5
 -thread with priority: 0.75
 -thread with priority: 0.5
 -thread with priority: 1.0
 -thread with priority: 0.75

On average, we will see that the threads with 0.5 and 0.75 priorities were called more often than the threads with 1.0 priorities after execution by looking at the output trace of the thread execution.

The following example shows how a simple Python script can schedule a Python thread based on a priority.

We have now seen some examples of Python threading and how to build threaded programs with many threads, scheduling priority, and the problems they solve. The use of start(), join(), time(), worker classes, and many other methods have also been demonstrated.

We hope you find this article helpful in understanding how to use the threads in Python.

Author: Abid Ullah
Abid Ullah avatar Abid Ullah avatar

My name is Abid Ullah, and I am a software engineer. I love writing articles on programming, and my favorite topics are Python, PHP, JavaScript, and Linux. I tend to provide solutions to people in programming problems through my articles. I believe that I can bring a lot to you with my skills, experience, and qualification in technical writing.

LinkedIn

Related Article - Python Thread