This article will directly give several examples to see how python implements multithreading. Previously in c + + An article In, the concept of multithreading is introduced. We won't continue to explain it here. If you are interested, you can click the link to understand it.
Python 3 uses two standard libraries_ Thread and threading provide support for threads because_ Thread is only compatible with Python 2 thread module, so it is recommended to use threading module to implement multithreading.
1. Introduction to some functions of threading
method | explain |
---|---|
threading.currentThread() | Returns the current thread variable |
threading.enumerate() | Returns a list of running threads |
threading.activeCount() | Returns the number of running threads, with the same result as len(threading.enumerate()). |
In addition to using methods, the thread module also provides thread class to process threads. Thread class provides the following methods:
method | explain |
---|---|
run() | Method used to represent thread activity. |
start() | Start thread activity. |
join([time]) | Wait until the thread aborts. Abort mode: exit normally or throw an unhandled exception - or an optional timeout occurs. |
isAlive() | Returns whether the thread is active. |
getName() | Returns the thread name. The first sub thread name is Thread-1, and the main thread name is MainThread |
setName() | Set the thread name. |
The following examples illustrate these functions. If you don't understand them, it doesn't matter. You can skip them temporarily and look back after learning all the contents.
import threading import time def thread_1(num): for i in range(num): #print(threading.currentThread()) #Current thread variable time.sleep(0.5) def thread_2(num): for i in range(num): #print(threading.currentThread()) #Current thread variable time.sleep(0.5) def main(): #args is the parameter corresponding to the function and exists in the form of tuple t_thread_1 = threading.Thread(target=thread_1, args=(5,)) t_thread_2 = threading.Thread(target=thread_2, args=(6,)) print("t_thread_1 isAlive(): ", t_thread_1.isAlive()) # Survival, False t_thread_1.start()#start-up t_thread_2.start()#start-up print("threading.enumerate(): ", threading.enumerate()) #The scope is a list print("threading.activeCount(): ", threading.activeCount()) #There are currently 3, including a main thread #Print information about a thread print("t_thread_1 isAlive(): ",t_thread_1.isAlive())#Survival, True print("t_thread_1 getName(): ", t_thread_1.getName()) # Get thread name Thread-1 print("t_thread_2 getName(): ", t_thread_2.getName()) # Get thread name Thread-2 t_thread_1.join() t_thread_2.join() print("t_thread_1 isAlive(): ", t_thread_1.isAlive()) # Survival, False print("Main thread end") if __name__ == '__main__': main()
2. Thread implementation - functions and classes
(1) Multithreading in function mode
import threading import time def thread_1(num): for i in range(num): print("Child thread 1:%d" % i) time.sleep(0.5) def thread_2(num): for i in range(num): print("Sub thread 2:%d" % i) time.sleep(0.5) def main(): #args is the parameter corresponding to the function and exists in the form of tuple t_thread_1 = threading.Thread(target=thread_1, args=(5,)) t_thread_2 = threading.Thread(target=thread_2, args=(6,)) t_thread_1.start()#start-up t_thread_2.start()#start-up if __name__ == '__main__': main()
(2) Class to implement multithreading
Steps:
- Import threading package
- Create a class that inherits threading Thread class, overriding the run method
- Create a class object and call the start() method to start the thread
import threading import time class MyThread(threading.Thread): def run(self): #rewrite for i in range(3): time.sleep(1) #self.name: the name of the thread starts from Thread-1 msg = "I'm "+self.name+' @ '+str(i) print(msg) def main(): for i in range(5):#Create 5 threads t = MyThread() t.start() if __name__ == '__main__': main()
3. Daemon thread and synchronization thread
(1) Non daemon thread - default thread
Non daemon thread: after the main thread executes all programs, the main thread does not exit, that is, it will not be destroyed until all sub threads complete their tasks. By default, the threads generated by python are non daemon threads.
When the thread is set to setDaemon(False), it indicates that the thread is a non daemon thread. The default is False. If it is set to True, it is a daemon thread.
In the following code, the child thread sets a delay, and the main thread will wait for the child thread to end.
import threading import time def my_thread(): time.sleep(2)#Delay 2s print('---Child thread end---') def main(): t1 = threading.Thread(target=my_thread) t1.start() print('---Main thread---end') if __name__ == '__main__': main()
Output:
---Main thread---end ---Child thread end---
(2) Daemon thread
The daemon thread, that is, when the main thread ends, the child thread will also die. To establish a daemon thread, you only need to set setDaemon(True).
Take the following code as an example. Because the child thread is set as the daemon thread and the delay of 2s is set in the child thread, the print of the child thread will not run after the main thread ends.
import threading import time def my_thread(): time.sleep(2) print('---Child thread end---') def main(): t1 = threading.Thread(target=my_thread) t1.setDaemon(True) t1.start() print('---Main thread---end') if __name__ == '__main__': main()
Output:
---Main thread---end
(3) Synchronous thread (join)
When the main thread encounters a synchronous thread, that is, when it encounters a join(), it will wait (that is, the main thread enters a blocked state and cannot move on) for all synchronous threads to complete execution, and then the main thread will continue to move on.
import threading import time def my_thread_1(): for i in range(2):#Run for 2s print('Sub thread 1 sleep{}second'.format(i + 1)) time.sleep(1) print('End of sub thread 1!!!') def my_thread_2(): for i in range(5):#Operate for 5s print('Sub thread 2 sleep{}second'.format(i + 1)) time.sleep(1) print('End of sub thread 2!!!') def main(): t1 = threading.Thread(target=my_thread_1) t1.start() t2 = threading.Thread(target=my_thread_2) t2.start() t1.join()#Wait for child thread 1 to end t2.join()#Wait for child thread 2 to end print('End of main thread!!!') if __name__ == '__main__': main()
Output:
Sub thread 1 sleep for the first second Sub thread 2 sleep for the first second Subthread 1 sleep second Sub thread 2 sleep second End of sub thread 1!!! Sub thread 2 sleep for the third second Sub thread 2 sleep for the 4th second Sub thread 2 sleep for the fifth second End of sub thread 2!!! End of main thread!!!
(4) Non daemon thread + synchronous thread
General situation: when we set a child thread as a non daemon thread and set it as a synchronous thread, that is, the synchronous thread join() mentioned above is the same. The main thread will wait for all synchronous threads to execute before proceeding, because by default, the created thread is a non daemon thread.
The second case: if the timeout parameter is added to the join, the main thread will wait for the timeout time of the child thread, and then continue to execute. The main thread will not exit after execution, but wait for the child thread to complete the task. Details can be found in the next section.
import threading import time def my_thread(): for i in range(2): print('Sub thread 1 sleep{}second'.format(i + 1)) time.sleep(1) print('End of child thread!!!') def main(): t1 = threading.Thread(target=my_thread) t1.setDaemon(False) t1.start() t1.join() #t1.join(timeout=1)#join sets timeout in seconds print('End of main thread!!!') if __name__ == '__main__': main()
Output:
Sub thread 1 sleep for the first second Subthread 1 sleep second End of child thread!!! End of main thread!!!
(5) Daemon + sync thread join
General situation: if it is a daemon thread and the timeout is not set in the join, the main thread will wait for the child thread to complete the task before continuing to execute.
The second case: if the timeout parameter is set for the join, after waiting for the time of the sub thread, the main thread continues to proceed. After the main thread finishes its task, it is found that the sub thread is still a guard thread, but at this time, the sub thread has not completed its task, and the main thread will not wait for it, but will be destroyed directly and automatically.
import threading import time def thread1(): for i in range(5): print('Sub thread sleep{}second'.format(i + 1)) time.sleep(1) print('End of child thread!!!') def main(): t1 = threading.Thread(target=thread1) t1.setDaemon(True) t1.start() t1.join(timeout=1)#Set timeout in seconds print('End of main thread!!!') if __name__ == '__main__': main()
Output:
Sub thread sleep 1st second End of main thread!!!
4. Mutex
Because threads are randomly scheduled, if multiple threads operate on an object at the same time, if the object is not well protected, the program result will be unpredictable. Therefore, we also call it "thread unsafe". In order to prevent the above situation, a mutually exclusive Lock appears.
Simple thread synchronization can be realized by using Lock and Rlock of Thread objects. Both objects have acquire and release methods. For data that needs to be operated by only one thread at a time, its operation can be placed between acquire and release methods.
import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, my_time): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.my_time = my_time def run(self): print ("Open thread: " + self.name) # Get lock for thread synchronization threadLock.acquire() print_time(self.name, self.my_time, 5)#Each run 5 times # Release the lock and start the next thread threadLock.release() def print_time(threadName, my_time, counter): while counter: time.sleep(my_time) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 threadLock = threading.Lock() threads = [] # Create a new thread thread1 = myThread(1, "Thread-1", 1)#Print once in 1s thread2 = myThread(2, "Thread-2", 2)#Print once in 2s # Open new thread thread1.start() thread2.start() # Add thread to thread list threads.append(thread1) threads.append(thread2) # Wait for all threads to complete for t in threads: t.join() print ("Exit main thread")
Output:
Open thread: Thread-1 Open thread: Thread-2 Thread-1: Wed Dec 29 14:41:14 2021 Thread-1: Wed Dec 29 14:41:15 2021 Thread-1: Wed Dec 29 14:41:16 2021 Thread-1: Wed Dec 29 14:41:17 2021 Thread-1: Wed Dec 29 14:41:18 2021 Thread-2: Wed Dec 29 14:41:20 2021 Thread-2: Wed Dec 29 14:41:22 2021 Thread-2: Wed Dec 29 14:41:24 2021 Thread-2: Wed Dec 29 14:41:26 2021 Thread-2: Wed Dec 29 14:41:28 2021 Exit main thread