Uncovering the mystery of multi-threaded out-of-order encapsulation

classmates! It’s class~

Table of Contents

  • Preface

  • The concept of concurrency and parallelism

  • Multithreading

  • Main thread and sub-thread

  • setDaemon() method

  • join() method

  • setName() and getName() methods

  • Multi-threaded disorder and encapsulation

  • disorder

  • encapsulation

Foreword

In today’s era of information explosion, efficient concurrent processing has become a challenge faced by many applications. Multi-threaded programming is a powerful solution in Maxthon Programming World. Through multi-threading, we can execute tasks concurrently and greatly improve program performance.

However, the disorder and encapsulation of multithreading bring a fascinating mystery to the program. The unpredictability of the execution order of threads seems to be weaving an intricate web of threads; and the independent execution of threads shows us a magnificent picture of multiple worlds operating independently at the same time.

The concept of concurrency and parallelism

In Python, concurrency and parallelism are two important concepts used to describe the way a program performs tasks.

Concurrency: The ability to execute multiple tasks at the same time. It can rotate through time slices on a single processor or switch between multiple processors to allow multiple tasks to execute alternately. Concurrency is a logical concept. Multiple tasks can be executed alternately, but not necessarily at the same time.

Parallel: The ability to execute multiple tasks at the same time. It uses the resources of multi-core processors or multiple machines to allocate multiple tasks to different processing units for execution at the same time. Parallelism is a physical concept where multiple tasks are truly executed simultaneously.

In Python, due to the existence of the Global Interpretation Lock (GIL), multi-threading cannot take full advantage of multi-core processors. Therefore, Python’s multi-threading is usually used to handle I/O-intensive tasks (such as network requests, file reading and writing, etc.). It can switch to other threads for execution while waiting for I/O operations to improve the response performance of the program.

If your task is computationally intensive, i.e. requires a lot of CPU calculations, then parallel programming may be more suitable. In Python, you can use the multiprocessing module to implement multi-process parallel programming. Each process has an independent GIL and can take advantage of multi-core processors.

It should be noted that concurrency and parallelism are not mutually exclusive and they can exist at the same time. In some cases, we can achieve more efficient programs and make full use of system resources by using multi-threads and multi-processes at the same time.

Multi-threading

Main thread and sub-thread

The main thread is the default thread of the program. It is responsible for executing the main tasks of the program. The ones created through the start() method are called child threads.

Here is a simple example code using the main thread and child threads:

import threading
import time

# Execution function of sub-thread
def thread_func():
   print("The child thread starts executing")
   time.sleep(2) # Simulate time-consuming operations
   print("Sub-thread execution ends")

# Main thread
print("The main thread starts executing")

#Create child thread object
t = threading.Thread(target=thread_func)

# Start child thread
t.start()

# The main thread continues to perform other tasks
time.sleep(1)
print("The main thread performs other tasks")

# Wait for the child thread to complete execution
t.join()

print("Main thread execution ends")

operation result:

 The main thread starts execution
 The child thread starts execution
 The main thread performs other tasks
 The execution of the child thread ends
 Main thread execution ends

In the above example, the main thread first prints a message, then creates the sub-thread object t, and calls t.start() to start the sub-thread. After the child thread starts executing, it prints a message, then sleeps for 2 seconds to simulate the time-consuming operation, and finally prints another message. At the same time, the main thread continues to perform other tasks and waits for 1 second before printing a message. The main thread uses t.join() to wait for the child thread to complete execution, and then prints a message to indicate the end of the main thread’s execution.

Run the above code, and you will see that the main thread and sub-threads execute alternately, and each thread has an independent execution process. The main thread and sub-threads can perform different tasks concurrently, realizing multi-threaded programming.

setDaemon() method

setDaemon() is a method provided by the Thread class, which is used to set the daemon attribute of the thread. After setting a thread as a daemon thread, the thread will automatically exit when the main thread exits, regardless of whether the daemon thread has completed execution.

The setDaemon() method accepts a Boolean parameter daemon, and the default value is False. If the daemon parameter is set to True, it means that the thread is set as a daemon thread; if it is set to False, it means that the thread is set as a non-daemon thread. daemon thread.

The following is an example of using the setDaemon() method, demonstrating the use of daemon threads:

 import threading
 import time
   
 # Execution function of sub-thread
 def thread_func():
    print("The child thread starts executing")
    time.sleep(5)
   print("Sub-thread execution ends")
   
 # Main thread
 print("The main thread starts executing")
   
 #Create child thread object
 t = threading.Thread(target=thread_func)
   
 #Set the child thread as a daemon thread
 t.setDaemon(True)
   
 # Start child thread
 t.start()
   
 # The main thread continues to perform other tasks
 time.sleep(1)
 print("The main thread performs other tasks")
   
 print("Main thread execution ends")

operation result:

 The main thread starts execution
 The child thread starts execution
 The main thread performs other tasks
 Main thread execution ends

In the above example, the main thread creates a child thread object t, and then sets the child thread as a daemon thread through t.setDaemon(True). The daemon thread will not prevent the main thread from exiting. Even if the daemon thread has not completed execution, the main thread can still exit normally. In this example, the sub-thread performed an operation that took 5 seconds, but because it was set as a daemon thread, the main thread exited immediately after completing other tasks without waiting for the daemon thread to complete execution.

It should be noted that the setDaemon() method must be called before the start() method, otherwise a RuntimeError exception will be thrown. In addition, once a thread is set as a daemon thread, its daemon properties cannot be modified. Therefore, it is generally recommended to set the guard attribute when creating a thread.

Daemon threads are very useful in certain situations, such as periodically executing some background tasks, monitoring the running status of the program, etc. When using daemon threads, you need to pay attention to the execution status of the daemon threads to ensure that they do not affect the normal operation of the program.

join() method

join() is a method provided by the Thread class, which is used to wait for the thread to complete execution.

The join() method can be called in the main thread to block the main thread until the specified thread completes execution. It pauses the execution of the main thread until the called thread completes execution or the specified timeout is exceeded.

The join() method can take an optional parameter timeout, which is used to set the maximum waiting time in seconds. If the timeout parameter is specified, the join() method will wait for the specified time. If the time exceeds and the thread has not completed execution, the main thread will continue to execute subsequent code. If the timeout parameter is not specified, the join() method will block the main thread until the called thread completes execution.

The following is an example using the join() method, demonstrating how to wait for the thread to complete execution:

 import threading
  import time
   
# Execution function of sub-thread
 def thread_func():
   print("The child thread starts executing")
   time.sleep(5)
   print("Sub-thread execution ends")
   
 # Main thread
 print("The main thread starts executing")
   
 #Create child thread object
 t = threading.Thread(target=thread_func)
   
 # Start child thread
 t.start()
  
 # Wait for the child thread to complete execution
 t.join()
  
 print("Main thread execution ends")
 

operation result:

The main thread starts execution
The child thread starts execution
The execution of the child thread ends
Main thread execution ends

In the above example, the main thread creates a sub-thread object t and then starts the sub-thread through t.start(). Next, the main thread calls the t.join() method and waits for the child thread to complete execution. During the execution of the sub-thread, the main thread will be blocked and will not continue to execute subsequent code until the sub-thread completes execution.

It should be noted that the join() method can be called immediately after starting the thread or at any time. If the called thread has completed execution, the join() method will return immediately. In addition, you can check whether the thread is still executing through the is_alive() method to avoid the main thread being blocked all the time.

The join() method is very useful when processing multi-threaded tasks. It can ensure that all thread tasks are completed before subsequent processing, ensuring synchronization and sequence between threads.

setName() and getName() methods

setName() and getName() are two methods provided by the Thread class for setting and getting the name of the thread.

The setName() method is used to set the name of the thread. It accepts a string parameter name, which represents the thread name to be set.

The getName() method is used to get the name of the thread. It returns a string representing the name of the thread.

The following is an example using the setName() and getName() methods to demonstrate how to set and get the name of a thread:

 import threading
   
 # Execution function of sub-thread
 def thread_func():
    print("The name of the child thread is:", threading.currentThread().getName())
   
 # Main thread
 print("The name of the main thread is:", threading.currentThread().getName())
   
 #Create child thread object
 t = threading.Thread(target=thread_func)
   
 #Set the name of the child thread
 t.setName("MyThread")
   
 # Get the name of the child thread
 print("The name of the child thread is:", t.getName())
   
 # Start child thread
 t.start()

operation result:

The name of the main thread is: MainThread
The name of the child thread is: MyThread
The name of the child thread is: MyThread

In the above example, the main thread gets its own name through threading.currentThread().getName() and prints it out. Then, a sub-thread object t is created, and the name of the sub-thread is set to “MyThread” through t.setName("MyThread"). Next, get the name of the child thread through t.getName() and print it out. Finally, start the child thread through t.start().

It is important to note that thread names are very useful for debugging and logging, and can help distinguish different threads. If the thread’s name is not explicitly set, the thread will automatically be assigned a default name.

In addition, the name of the thread can be set by directly modifying the name property of the Thread object. For example, t.name = "MyThread" can achieve the same effect as t.setName("MyThread").

In actual multi-thread programming, you can usually set a meaningful name for a thread through the setName() method to facilitate thread tracking and debugging. Through the getName() method, you can obtain the name of the thread when needed for further processing and recording.

Multi-threading disorder and encapsulation

Unordered

In Python multi-threaded programming, out-of-order (no guaranteed sequential execution) and encapsulation (independent threads) are two important concepts.

Disorder means that in the process of multi-thread execution, the execution order of each thread is uncertain, and no sequential assumptions can be made.

Since the thread scheduling mechanism is determined by the operating system, the execution order of threads may be affected by a variety of factors, such as the operating system’s scheduling policy, thread priority, etc. Therefore, the execution results of multi-threaded programs may be different in different running environments.

Here is an example of using multi-threading to demonstrate disorder, where two threads are created that alternately perform printing operations:

 import threading
   
 #Execution function of thread
 def thread_func(name):
    for i in range(5):
        print(f"Thread {<!-- -->name} executes, prints {<!-- -->i}")
   
 #Create thread object
 t1 = threading.Thread(target=thread_func, args=("Thread 1",))
 t2 = threading.Thread(target=thread_func, args=("Thread 2",))
   
 # Start thread
 t1.start()
 t2.start()

Running the above code, the possible output results are as follows:

 Thread 1 executes and prints 0
 Thread 2 executes and prints 0
 Thread 1 executes and prints 1
 Thread 2 executes and prints 1
Thread 1 executes and prints 2
 Thread 2 executes and prints 2
Thread 2 executes and prints 3
 Thread 1 executes and prints 3
 Thread 2 executes and prints 4
 Thread 1 executes and prints 4

It can be seen that the execution order of the two threads is uncertain, and they will alternately perform different tasks. This is the embodiment of disorder.

Encapsulation

Encapsulation means that each thread is an independent execution unit, and they have independent call stacks and execution contexts. Threads do not interfere with each other or share variables (unless specifically designed). This encapsulation ensures thread safety because each thread can perform its own tasks independently without interfering with each other.

The following is an example of using multi-threading to demonstrate encapsulation, in which two threads operate on shared variables separately without interfering with each other:

 import threading
   
 # Shared variables
 counter = 0
   
 # Lock  
 lock = threading.Lock()
   
 #Execution function of thread
 def thread_func():
    global counter
    for _ in range(100000):
        with lock:
            counter + = 1
  
 #Create thread object
t1 = threading.Thread(target=thread_func)
 t2 = threading.Thread(target=thread_func)
 
# Start thread
 t1.start()
 t2.start()
   
 # Wait for the thread to complete execution
 t1.join()
 t2.join()
 
 print("Final result:", counter)
 

Running the above code, the output result is that the two threads accumulate the shared variable counter, and the final output result will be close to 200000. Due to the use of a lock mechanism, each operation on counter is guaranteed to be atomic, avoiding issues of race conditions and data inconsistency. This is the embodiment of encapsulation.

Ding ding bell, get out of class is over~ I wish everyone a happy life

Finally:

Python learning materials

If you want to learn Python to help you automate your office, or are preparing to learn Python or are currently learning it, you should be able to use the following and get it if you need it.

① A roadmap for learning Python in all directions, knowing what to learn in each direction
② More than 100 Python course videos, covering essential basics, crawlers and data analysis
③ More than 100 Python practical cases, learning is no longer just theory
④ Huawei’s exclusive Python comic tutorial, you can also learn it on your mobile phone
⑤Real Python interview questions from Internet companies over the years, very convenient for review

There are ways to get it at the end of the article

1. Learning routes in all directions of Python

The Python all-direction route is to organize the commonly used technical points of Python to form a summary of knowledge points in various fields. Its usefulness is that you can find corresponding learning resources according to the above knowledge points to ensure that you learn more comprehensively.

2. Python course video

When we watch videos and learn, we can’t just move our eyes and brain but not our hands. The more scientific learning method is to use them after understanding. At this time, hands-on projects are very suitable.

3. Python practical cases

Optical theory is useless. You must learn to follow along and practice it in order to apply what you have learned to practice. At this time, you can learn from some practical cases.

Four Python Comics Tutorial

Use easy-to-understand comics to teach you to learn Python, making it easier for you to remember and not boring.

5. Internet company interview questions

We must learn Python to find a high-paying job. The following interview questions are the latest interview materials from first-tier Internet companies such as Alibaba, Tencent, Byte, etc., and Alibaba bosses have given authoritative answers. After finishing this set I believe everyone can find a satisfactory job based on the interview information.


This complete version of the complete set of Python learning materials has been uploaded to CSDN. If friends need it, you can also scan the official QR code of csdn below or click on the WeChat card at the bottom of the homepage and article to get the method. [Guaranteed 100% free]

syntaxbug.com © 2021 All Rights Reserved.