Multithreading is similar to executing multiple different programs at the same time. Multithreading has the following advantages:
If you are interested in learning Python together, click the following icon to add a group:
- Using threads, you can put tasks in programs that occupy a long time in the background.
- The user interface can be more attractive. For example, if the user clicks a button to trigger the processing of some events, a progress bar can pop up to display the processing progress.
- The program may run faster.
- In the implementation of some waiting tasks, such as user input, file reading and writing, network sending and receiving data, threads are more useful. In this case, we can release some precious resources, such as memory occupation and so on.
Each independent thread has an entry for program operation, sequential execution sequence and program exit. However, threads cannot be executed independently. They must be stored in the application, and the application provides multiple thread execution control.
Each thread has its own set of CPU registers, called the thread context, which reflects the state of the CPU register in which the thread last ran.
Instruction pointer and stack pointer registers are the two most important registers in the thread context. Threads always run in the process context. These addresses are used to mark the memory in the process address space of the thread.
- Threads can be preempted (interrupted).
- Threads can be temporarily suspended (also known as sleep) while other threads are running - this is the concession of threads.
Threads can be divided into:
- **Kernel thread: * * created and revoked by the operating system kernel.
- **User thread: * * thread implemented in user program without kernel support.
Two modules commonly used in Python 3 threads are:
- _thread
- Threading (recommended)
Thread module is obsolete. Users can use threading module instead. Therefore, the "thread" module can no longer be used in Python 3. For compatibility, python 3 renamed the thread to "_thread".
Start learning Python threads
There are two ways to use threads in Python: functions or wrapping thread objects with classes.
Functional: calling_ Start in thread module_ new_ Thread() function to generate a new thread. The syntax is as follows:
_thread.start_new_thread ( function, args[, kwargs] )
Parameter Description:
- Function - thread function.
- args - the parameter passed to the thread function. It must be of type tuple.
- kwargs - optional parameter.
example:
#!/usr/bin/python3 import _thread import time # Define a function for the thread def print_time( threadName, delay): count = 0 while count < 5: time.sleep(delay) count += 1 print ("%s: %s" % ( threadName, time.ctime(time.time()) )) # Create two threads try: _thread.start_new_thread( print_time, ("Thread-1", 2, ) ) _thread.start_new_thread( print_time, ("Thread-2", 4, ) ) except: print ("Error: Unable to start thread") while 1: pass
Execute the above procedure and the output results are as follows:
Thread-1: Wed Apr 6 11:36:31 2016 Thread-1: Wed Apr 6 11:36:33 2016 Thread-2: Wed Apr 6 11:36:33 2016 Thread-1: Wed Apr 6 11:36:35 2016 Thread-1: Wed Apr 6 11:36:37 2016 Thread-2: Wed Apr 6 11:36:37 2016 Thread-1: Wed Apr 6 11:36:39 2016 Thread-2: Wed Apr 6 11:36:41 2016 Thread-2: Wed Apr 6 11:36:45 2016 Thread-2: Wed Apr 6 11:36:49 2016
After executing the above procedure, you can press ctrl-c to exit.
Thread module
Python 3 uses two standard libraries_ thread and threading provide support for threads.
_ thread provides low-level, original threads and a simple lock. Compared with threading module, its function is still relatively limited.
The threading module contains_ In addition to all methods in the thread module, other methods are also provided:
- threading.currentThread(): returns the current thread variable.
- threading.enumerate(): returns a list of running threads. Running refers to threads after starting and before ending, excluding threads before starting and after termination.
- threading.activeCount(): returns the number of running threads, which is the same 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:
-
run(): the method used to represent thread activity.
-
start():
Start thread activity.
-
join([time]): wait until the thread aborts. This blocks the calling thread until the thread's join() method is called to abort - exit normally or throw an unhandled exception - or an optional timeout occurs.
-
isAlive(): Returns whether the thread is active.
-
getName(): returns the thread name.
-
setName(): sets the thread name.
Creating threads using the threading module
We can create a new subclass by directly inheriting from threading.Thread, and instantiate and call the start() method to start the new thread, that is, it calls the run() method of the thread:
#!/usr/bin/python3 import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Start thread:" + self.name) print_time(self.name, self.counter, 5) print ("Exit thread:" + self.name) def print_time(threadName, delay, counter): while counter: if exitFlag: threadName.exit() time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 # Create a new thread thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # Open new thread thread1.start() thread2.start() thread1.join() thread2.join() print ("Exit main thread")
The results of the above procedures are as follows:
Start thread: Thread-1 Start thread: Thread-2 Thread-1: Wed Apr 6 11:46:46 2016 Thread-1: Wed Apr 6 11:46:47 2016 Thread-2: Wed Apr 6 11:46:47 2016 Thread-1: Wed Apr 6 11:46:48 2016 Thread-1: Wed Apr 6 11:46:49 2016 Thread-2: Wed Apr 6 11:46:49 2016 Thread-1: Wed Apr 6 11:46:50 2016 Exit thread: Thread-1 Thread-2: Wed Apr 6 11:46:51 2016 Thread-2: Wed Apr 6 11:46:53 2016 Thread-2: Wed Apr 6 11:46:55 2016 Exit thread: Thread-2 Exit main thread
Thread synchronization
If multiple threads modify a data together, unexpected results may occur. In order to ensure the correctness of the data, multiple threads need to be synchronized.
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. As follows:
The advantage of multithreading is that it can run multiple tasks at the same time (at least it feels like this). However, when threads need to share data, there may be a problem of data synchronization.
Consider this situation: all elements in a list are 0, the thread "set" changes all elements to 1 from back to front, and the thread "print" is responsible for reading and printing the list from front to back.
Then, when the thread "set" starts to change, the thread "print" will print the list, and the output will be half 0 and half 1, which is the asynchrony of data. In order to avoid this situation, the concept of lock is introduced.
The lock has two states - locked and unlocked. Whenever a thread, such as "set", wants to access shared data, it must first obtain a lock; If another thread, such as "print", has been locked, the thread "set" is suspended, that is, synchronization blocking; Wait until the thread "print" is accessed and the lock is released, and then let the thread "set" continue.
After such processing, when printing the list, either all 0 or all 1 will be output, and the embarrassing scene of half 0 and half 1 will not appear again.
example:
#!/usr/bin/python3 import threading import time class myThread (threading.Thread): def __init__(self, threadID, name, counter): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.counter = counter def run(self): print ("Open thread: " + self.name) # Get lock for thread synchronization threadLock.acquire() print_time(self.name, self.counter, 3) # Release the lock and start the next thread threadLock.release() def print_time(threadName, delay, counter): while counter: time.sleep(delay) print ("%s: %s" % (threadName, time.ctime(time.time()))) counter -= 1 threadLock = threading.Lock() threads = [] # Create a new thread thread1 = myThread(1, "Thread-1", 1) thread2 = myThread(2, "Thread-2", 2) # 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")
Execute the above procedure, and the output result is:
Open thread: Thread-1 Open thread: Thread-2 Thread-1: Wed Apr 6 11:52:57 2016 Thread-1: Wed Apr 6 11:52:58 2016 Thread-1: Wed Apr 6 11:52:59 2016 Thread-2: Wed Apr 6 11:53:01 2016 Thread-2: Wed Apr 6 11:53:03 2016 Thread-2: Wed Apr 6 11:53:05 2016 Exit main thread
Thread priority Queue
Python's Queue module provides synchronized and thread safe Queue classes, including FIFO (first in first out) Queue, LIFO (last in first out) Queue, LifoQueue, and priority Queue PriorityQueue.
These queues implement lock primitives and can be used directly in multithreading. Queues can be used to achieve synchronization between threads.
Common methods in the Queue module:
- Queue.qsize() returns the size of the queue
- Queue.empty() returns True if the queue is empty, otherwise False
- Queue.full() returns True if the queue is full, otherwise False
- Queue.full corresponds to the maxsize size
- Queue.get([block[, timeout]]) gets the queue and timeout the waiting time
- Queue.get_nowait() is equivalent to Queue.get(False)
- Queue.put(item) writes to the queue and timeout the waiting time
- Queue.put_nowait(item) is equivalent to Queue.put(item, False)
- Queue.task_done() after completing a task, queue. Task_ The done () function sends a signal to the queue where the task has been completed
- Queue.join() actually means waiting until the queue is empty before doing anything else
example:
#!/usr/bin/python3 import queue import threading import time exitFlag = 0 class myThread (threading.Thread): def __init__(self, threadID, name, q): threading.Thread.__init__(self) self.threadID = threadID self.name = name self.q = q def run(self): print ("Open thread:" + self.name) process_data(self.name, self.q) print ("Exit thread:" + self.name) def process_data(threadName, q): while not exitFlag: queueLock.acquire() if not workQueue.empty(): data = q.get() queueLock.release() print ("%s processing %s" % (threadName, data)) else: queueLock.release() time.sleep(1) threadList = ["Thread-1", "Thread-2", "Thread-3"] nameList = ["One", "Two", "Three", "Four", "Five"] queueLock = threading.Lock() workQueue = queue.Queue(10) threads = [] threadID = 1 # Create a new thread for tName in threadList: thread = myThread(threadID, tName, workQueue) thread.start() threads.append(thread) threadID += 1 # Fill queue queueLock.acquire() for word in nameList: workQueue.put(word) queueLock.release() # Waiting for queue to clear while not workQueue.empty(): pass # Notify the thread when to exit exitFlag = 1 # Wait for all threads to complete for t in threads: t.join() print ("Exit main thread")
Execution results of the above procedures:
Open thread: Thread-1 Open thread: Thread-2 Open thread: Thread-3 Thread-3 processing One Thread-1 processing Two Thread-2 processing Three Thread-3 processing Four Thread-1 processing Five Exit thread: Thread-3 Exit thread: Thread-2 Exit thread: Thread-1 Exit main thread
If you are interested in learning Python together, click the following icon to add a group: