Python Multithreading

Multithreading in Python

This document covers the basics of multithreading in the Python programming language. Multiprocessing as well as multithreading is a method to achieve multitasking. In multithreading, the concept of a thread is used. First, let’s understand the concept of a thread in computer architecture.

What is a Process in Python?

In computing, a process is an instance of a running computer program. Every process has three basic components.

  • An executable program.
  • Relevant data required by the program (variables, workspace, buffers, etc.)
  • The execution context of the program (process state)

Introduction to Python Threading

A thread is an entity within a process that can be scheduled for execution. It is also the smallest unit of processing that can be executed by the OS (operating system). Simply put, a thread is a sequence of instructions within a program that can be executed independently from other code. For simplification, a thread can be considered simply a subset of a process. Threads contain all this information in a Thread Control Block (TCB).

  • Thread Identifier: A unique ID (TID) is assigned to each new thread.
  • Stack Pointer: Points to the thread stack of the process. The stack contains local variables defined within the thread’s scope.
  • Program Counter: A register that holds the address of the instruction currently being executed by the thread.
  • Thread State: Can be running, ready, waiting, starting, or completed.
  • Set of Registers for the Thread: Registers assigned to the thread for computation.
  • Pointer to Parent Process: A pointer to the process control block (PCB) of the process that the thread belongs to.

Understanding how to implement multithreading can significantly enhance the performance of I/O-bound and certain types of network applications. However, mastering this concept requires a clear understanding of some advanced features of Python. For those looking to advance these skills further, a free Python course includes a dedicated module on multithreading.

To understand the relationship between processes and threads, consider the diagram below.

Relationship Between Processes and Threads

There can be multiple threads within a single process in the following cases:

  • Each thread includes its ownset of registers andlocal variables (stored on the stack).
  • All threads in a process shareglobal variables (stored in the heap) andprogram code.

To understand how multiple threads exist in memory, consider the diagram below.

Multiple Threads in Memory

Introduction to Python Threading

Multithreading is defined as the ability of a processor to execute multiple threads simultaneously. In a simple single-core CPU, this is achieved using frequent context switching between threads. This is calledcontext switching. In context switching, the state of a thread is saved each time an interrupt (due to I/O or manual setup) occurs, and the state of another thread is loaded. The context switching happens so frequently that it appears as if all threads are running in parallel (this is referred to asmultitasking).

Look at the diagram below that contains two active threads in a process. 

Multithreading

Multithreading in Python

The threading module in Python provides a very simple and intuitive API for creating multiple threads in a program. Let’s understand multithreading code step by step.

Step 1: Import the module

First, import the threading module.

import threading

Step 2: Create Threads

To create a new thread, create an object of the Thread class. Use ‘target’ and ‘args’ as parameters. The target is the function that will be executed by the thread, and args are the arguments that will be passed to the target function .

t1 = threading.Thread(target, args)
t2 = threading.Thread(target, args)

Step 3: Start the Thread

To start a thread, use thestart() method of the Thread class.

t1.start()
t2.start()

Step 4: End Thread Execution

Once the threads are started, the current program (think of it as the main thread) continues to execute as well. To stop the execution of the current program until the threads complete, use thejoin() method.

t1.join()
t2.join()

As a result, the current program waits fort1 to finish first, and then waits fort2 to complete. Once the tasks are done, the remaining statements of the current program are executed.

Example:

Let’s look at a simple example using the threading module.

This code demonstrates how to calculate the square and cube of a number simultaneously using Python’s threading module. Two threads are created to perform these calculations. The results are printed in parallel before the program prints “Done!” after both threads have completed. Threading is used to achieve parallelism when handling computation-intensive tasks and enhance program performance. t1 t2

Python

import threading


def print_cube(num):
    print("Cube: {}" .format(num * num * num))


def print_square(num):
    print("Square: {}" .format(num * num))


if __name__ =="__main__":
    t1 = threading.Thread(target=print_square, args=(10,))
    t2 = threading.Thread(target=print_cube, args=(10,))

    t1.start()
    t2.start()

    t1.join()
    t2.join()

    print("Done!")

Output

Square: 100
Cube: 1000
Done!

Output:

Square: 100
Cube: 1000
Done!

To better understand how the above program works, consider the diagram below. 

Multithreading

Example:

This example uses theos.getpid() function to get the ID of the current process. Thethreading.main_thread() function is used to retrieve the main thread object. Under normal conditions, the main thread is the thread in which the Python interpreter was started. Thename attribute of the thread object is used to obtain the thread’s name. Then, thethreading.current_thread() function is used to retrieve the current thread object.

Consider the following Python program that prints the thread name and its associated process for each task. 

This code demonstrates how to run two tasks simultaneously using Python’s threading module. The main program starts two threads, each executing a specific task. The threads run in parallel, and the code provides information about the process ID and thread name. The module is used to access process IDs, and the module is used to manage threads and thread execution. t1 t2osthreading’

Python

import threading
import os

def task1():
    print("Task 1 assigned to thread: {}".format(threading.current_thread().name))
    print("ID of process running task 1: {}".format(os.getpid()))

def task2():
    print("Task 2 assigned to thread: {}".format(threading.current_thread().name))
    print("ID of process running task 2: {}".format(os.getpid()))

if __name__ == "__main__":

    print("ID of process running main program: {}".format(os.getpid()))

    print("Main thread name: {}".format(threading.current_thread().name))

    t1 = threading.Thread(target=task1, name='t1')
    t2 = threading.Thread(target=task2, name='t2')

    t1.start()
    t2.start()

    t1.join()
    t2.join()

Output

ID of process running main program: 19
Main thread name: MainThread
Task 1 assigned to thread: t1
ID of process running task 1: 19
Task 2 assigned to thread: t2
ID of process running task 2: 19

Output:

ID of process running main program: 1141
Main thread name: MainThread
Task 1 assigned to thread: t1
ID of process running task 1: 1141
Task 2 assigned to thread: t2
ID of process running task 2: 1141

The diagram below clarifies the above concepts.

Multithreading

In conclusion, we briefly introduced multithreading in Python. The next article in this series will cover synchronization between multiple threads.  Multithreading in Python | Set 2 (Synchronization) 

Python Thread Pool

A thread pool is a collection of threads that can be reused to execute multiple tasks, which are created in advance. The Concurrent.futures module in Python provides the ThreadPoolExecutor class, which allows easy creation and management of thread pools. 

This example defines a function worker that will be executed in the threads. It creates a ThreadPoolExecutor with a maximum of 2 worker threads. The two tasks are then submitted to the pool using the submit method. The pool manages the execution of tasks by the worker threads. To wait for all tasks to complete before the main thread continues, the shutdown method is used.

Multithreading helps improve a program’s efficiency and responsiveness. However, it is crucial to be cautious when working with threads to avoid issues like race conditions and deadlocks.

This code runs two worker tasks simultaneously using a thread pool created by concurrent.futures. It allows efficient parallel processing of tasks in a multithreaded environment. concurrent.futures.ThreadPoolExecutorpool.shutdown(wait=True)

Python

import concurrent.futures

def worker():
    print("Worker thread running")

pool = concurrent.futures.ThreadPoolExecutor(max_workers=2)

pool.submit(worker)
pool.submit(worker)

pool.shutdown(wait=True)

print("Main thread continuing to run")

Output

Worker thread running
Worker thread running
Main thread continuing to run

Multithreading in Python – FAQ

What is Multithreading in Python?

Multithreading in Python involves executing multiple threads simultaneously within a single process to achieve parallelism and utilize multiple CPU cores.

Is Python suitable for Multithreading?

Due to the GIL (Global Interpreter Lock), which limits Python to execute only one thread at a time for CPU-bound tasks, Python is suitable for using multithreading for I/O-bound tasks. For CPU-bound tasks, multiprocessing is often more effective.

Which module is used for Multithreading in Python?

The‘threading’ module is used for multithreading in Python.

What are the various types of threads in Python?

The two main types of threads in Python are:

  • Main Thread: The initial thread that runs when the program starts.
  • Daemon Threads: Background threads that automatically terminate when the main thread exits.
  • Non-Daemon Threads: Threads that continue to run until their tasks are complete, even if the main thread exits.

How many threads can Python have for Multithreading?

There is no fixed limit on the number of threads in Python, but the actual limit is determined by system resources and the GIL, so having too many threads may degrade performance. Generally, Python applications use dozens to hundreds of threads, but for intensive tasks, it’s recommended to use multiprocessing.

McDonald’s Pyramid Location in Egypt

Daily life in Egypt is always filled with amazing stories. Interestingly, the McDonald’s I am introducing this time boasts a unique architecture that harmoniously combines Egypt’s traditional beauty with modern convenience. This is the “Pyramid McDonald’s.”

This McDonald’s, like the numerous pyramids in Egypt, has an engraved triangular shape. Moreover, it is closely related not only to the shape of the building but also to local tourist attractions.

Unlike typical fast food chains, this McDonald’s has established itself in harmony with Egyptian culture. They offer a unique experience to both locals and tourists by blending traditional Egyptian cuisine with the globally famous McDonald’s menu.

Furthermore, Pyramid McDonald’s is very popular among tourists. Many people visit this place to enjoy a special experience, take photos, and share them on social media, creating unique memories.

Ultimately, Pyramid McDonald’s holds a meaning beyond just a fast food restaurant. It serves as a bridge connecting Egypt’s traditions and modernity, providing tourists with a unique experience. If you visit Egypt, I highly recommend you drop by.

Python Data Types – Boolean

Python Data Types – Boolean

Learning Python

2024-10-16 01:46:52


Python Boolean Data Type

Python Boolean Data Type

The Boolean data type in Python can only have two values: True and False. The Boolean data type is mainly used in conditional statements to represent true and false.

a = True
b = False

Characteristics of Boolean Data Type

1. Usage in Conditional Statements

The Boolean data type is mainly used in conditional statements and loops to determine conditions. For example, in an if statement, it determines the flow of execution based on whether a specific condition is true or false.

is_raining = True

if is_raining:
    print("Take an umbrella!")
else:
    print("No umbrella needed.")

2. Result of Comparison Operations

The Boolean data type is often used as a result of comparison operators. Comparison operators compare two values and return true or false.

x = 10
y = 20

print(x == y)  # False
print(x < y)   # True
print(x != y)  # True

3. Logical Operators

The Boolean data type can perform complex logical operations using the logical operators and, or, and not.

a = True
b = False

# and operator: True only if both conditions are true
print(a and b)  # False

# or operator: True if at least one condition is true
print(a or b)   # True

# not operator: flips the value
print(not a)    # False

4. Operations with Boolean Data Type and Other Data Types

In Python, the Boolean data type can be used like integers. True is considered as 1, and False is considered as 0. This allows for simple arithmetic operations.

print(True + 1)   # 2
print(False + 5)  # 5

5. Values That Determine True and False

In Python, various data types are considered true or false in conditional statements. Generally, non-empty values are considered true, while empty values or 0 are considered false.

  • 0, None, empty string "", empty list [], empty set {}, etc., are considered False.
  • All other values are considered True.
if 0:
    print("True.")
else:
    print("False.")  # Output: False.

if "Hello":
    print("True.")  # Output: True.

Summary

  • The Boolean data type can only have two values: True and False.
  • It is used to evaluate logical conditions in conditional statements and loops.
  • Comparison and logical operators can be used to assess true (True) and false (False) values.
  • True is treated as 1, and False as 0, allowing for use in arithmetic operations.
  • Different data types are considered True if they have values and False if they don't.

The Boolean data type plays a central role in decision-making and logical operations in Python. It can be used to control the flow of programs and perform various tasks based on conditions.


Python Data Type: Dictionary

Python Data Types: Dictionary

Python Study

2024-10-16 01:44:25


Python Dictionary Data Type

Python Dictionary Data Type

In Python, a Dictionary is a mapping data type that stores data in key and value pairs. Dictionaries are defined using curly braces {}, and each element consists of a key and a value. For example:

my_dict = {"name": "Alice", "age": 25, "city": "New York"}

Features of Dictionary

1. Stored in Key-Value Pairs

Dictionaries store each element as a key-value pair, and keys must be unique. Keys must be immutable types (e.g., strings, numbers, tuples), while values can be of any data type.

person = {"name": "Bob", "age": 30, "job": "Developer"}
print(person["name"])  # 'Bob'
print(person["age"])   # 30

2. Modifying Dictionaries

Dictionaries are mutable, which means you can add, modify, or delete elements. You can add new key-value pairs or modify existing values.

my_dict = {"name": "Alice", "age": 25}
my_dict["city"] = "New York"  # Add a new key-value pair
my_dict["age"] = 26            # Modify existing value
print(my_dict)  # {'name': 'Alice', 'age': 26, 'city': 'New York'}

3. Deleting Dictionary Elements

You can delete a specific element from a dictionary using the del keyword or the pop() method.

my_dict = {"name": "Alice", "age": 25, "city": "New York"}
del my_dict["age"]           # Delete 'age' key
print(my_dict)  # {'name': 'Alice', 'city': 'New York'}

city = my_dict.pop("city")   # Delete 'city' key and return its value
print(city)      # 'New York'
print(my_dict)   # {'name': 'Alice'}

4. Dictionary Methods

Dictionaries provide various methods for easy manipulation of elements:

  • dict.keys(): Returns all keys in the dictionary.
  • dict.values(): Returns all values in the dictionary.
  • dict.items(): Returns all key-value pairs as tuples.
  • dict.get(key): Returns the value corresponding to the key; returns None if the key does not exist.
  • dict.update(other_dict): Adds or updates elements from another dictionary.
my_dict = {"name": "Alice", "age": 25}
print(my_dict.keys())    # dict_keys(['name', 'age'])
print(my_dict.values())  # dict_values(['Alice', 25])
print(my_dict.items())   # dict_items([('name', 'Alice'), ('age', 25)])

print(my_dict.get("city"))  # None
my_dict.update({"city": "New York", "age": 26})
print(my_dict)  # {'name': 'Alice', 'age': 26, 'city': 'New York'}

5. Iterating Over Dictionaries

You can use loops to iterate over keys, values, or key-value pairs in a dictionary.

my_dict = {"name": "Alice", "age": 25, "city": "New York"}

# Iterating over keys
for key in my_dict:
    print(key)

# Iterating over values
for value in my_dict.values():
    print(value)

# Iterating over key-value pairs
for key, value in my_dict.items():
    print(f"{key}: {value}")

6. Nested Dictionaries

Dictionaries can have other dictionaries as values, which are called nested dictionaries. Nested dictionaries are useful for structuring complex data.

nested_dict = {
    "person1": {"name": "Alice", "age": 25},
    "person2": {"name": "Bob", "age": 30}
}
print(nested_dict["person1"]["name"])  # 'Alice'
print(nested_dict["person2"]["age"])   # 30

Summary

  • Dictionaries are a mapping data type that stores data in key-value pairs.
  • Dictionaries are mutable and allow adding, modifying, and deleting elements.
  • Methods like keys(), values(), items() allow access to dictionary elements.
  • Dictionaries can be iterated over with loops to access keys, values, or key-value pairs.
  • Nested dictionaries can be used to structure complex data.

Dictionaries are one of the most important data types in Python, useful for efficiently storing and manipulating data. Explore the various features of dictionaries to handle complex data!


Introduction to Basic Python Syntax

Introduction to Basic Python Syntax

Learning Python

2024-10-15 16:43:13


Python Basic Syntax Introduction

Python is an easy and powerful programming language, and thanks to its concise and intuitive syntax, many people choose it as their first programming language. Additionally, with a variety of libraries and robust community support, it is suitable for everyone from beginners to experts. In this article, we’ll explore the basic syntax of Python.

1. Variables and Data Types

In Python, when declaring variables, no additional keywords are needed. Simply assign a name and value to the variable.

The name of a variable must start with a letter or underscore and is case-sensitive. For example, x and X are considered different variables.

x = 5          # Integer variable
name = “Alice” #
String variable
pi = 3.14      #
Float variable

is_valid = True # Boolean variable

Python is a dynamic typed language, so you don’t need to specify the type of a variable as it is determined automatically. This allows for flexible programming, where the value of a variable can change as needed.

2. Basic Operators

In Python, you can perform calculations using basic arithmetic operators. In addition, subtraction, multiplication, and division, you can also use modulus (%) and exponentiation (**) operators.

·       Addition: +

·       Subtraction:

·       Multiplication: *

·       Division: /

·       Modulus: %

·       Exponentiation: **

a = 10
b = 3
print(a + b)  # 13
print(a b)  # 7
print(a * b)  # 30
print(a / b)  # 3.333…
print(a % b)  # 1
print(a ** b) # 1000

3. Conditional Statements

Conditional statements are used to control the flow of the program. Using the keywords if, elif, and else, you can specify conditions, and indentation is used to define code blocks. Python’s conditional statements are more concise and intuitive.

age = 18
if age >= 20:
    print(
Adult..”)
elif age >= 13:
    print(
Teenager..”)
else:
    print(
Child..”)

In Python, you can use comparison operators and logical operators to create complex conditions.

is_student = True
if age >= 13 and is_student:
    print(
Student..”)

4. Loops

Loops are used to repeat specific tasks. In Python, there are for and while loops. The for loop is generally used to iterate over lists or ranges, while the while loop continues as long as the condition is true.

# for loop example
for i in range(5):
    print(i)  # 0, 1, 2, 3, 4

#
List iteration
fruits = [“apple”, “banana”, “cherry”]
for fruit in fruits:
    print(fruit)  # apple, banana, cherry

# while
loop example
count = 0
while count < 3:
    print(count)  # 0, 1, 2
    count += 1

Loops can be controlled using the break and continue keywords. break completely exits the loop, while continue skips the current iteration and moves to the next iteration.

for i in range(5):
    if i == 3:
        break
    print(i)  # 0, 1, 2

for i in range(5):
    if i == 3:
        continue
    print(i)  # 0, 1, 2, 4

5. Functions

Functions are used to increase code reusability and to divide logic into logical units. Use the keyword def to define a function, and you can pass arguments to it. A function can also return values , allowing for the implementation of more complex logic.

def greet(name):
    print(f”Hello, {name}!”)

greet(“Alice”)  # Hello, Alice!

def add(a, b):
    return a + b

result = add(3, 5)
print(result)  # 8

Functions can also have default arguments.

def greet(name=“Guest”):
    print(f”Hello, {name}!”)

greet()          # Hello, Guest!
greet(“Bob”)     # Hello, Bob!

6. Lists and Dictionaries

·       List: A list is a data structure that stores multiple values in one variable. Lists are ordered, and each element can be accessed through its index. Lists are also mutable, which means they can be modified, added , or deleted.