Concurrent programming
- Concurrent (pseudo): Because of the speed of execution, it is not perceptible
- Parallel (true): create 10 simultaneous operations
thread
- Single-process, single-threaded applications
- print('666')
- What exactly is a thread?What is a process
- Python does not have this on its own; the threads and processes of the operating system (pseudothreads) called in Python
- Multithreaded
- Minimum unit of work
- Share all resources in the process
- Each thread can share a few tasks and ultimately complete the final result
python multithreading principle: python multithreading is actually a pseudo multithreading (multiple threads running on a 1-core CPU, fast switching causes the illusion of simultaneous execution)
Code
import threading def func(arg): print(arg) th = threading.Thread(target=func) th.start() print('end')
One application: software
- The default is that a program has only one process
- There can be multiple processes (only one by default), and one process can create multiple threads (the default one).
In the case of Python multithreading:
- Computing intensive operations: inefficient (GIL locks)
- IO operation: High efficiency
In Python multi-process scenarios:
- Computing intensive operations: High efficiency (wasted space)
- IO operations: High efficiency (waste of resources)
When writing Python later:
- IO-intensive multi-threaded: file/input/output/socket
- Computing intensive with multiple processes:
process
- Open up memory independently
- Data isolation between processes
Note: Processes are designed to provide an environment for threads to work
Threads and processes in Python (GIL locks)
GIL lock, global interpreter lock.Used to limit one thread to be scheduled by the CPU at a time in a process
Extension: Default GIL lock after executing 100 cpu instructions (expiration time).
Use of threads
Multithread Basic Usage
- Basic examples
import threading def func(arg): print(arg) th = threading.Thread(target=func) th.start() print('end')
- Test example (the main thread executes its own code by default, and then waits for the child thread to finish executing)
import time import threading def func(arg): time.sleep(arg) print(arg) t1 = threading.Thread(target=func,args=(3,)) t1.start() t2 = threading.Thread(target=func,args=(9,)) t2.start() print('end')
- jojn method:
import time import threading def func(arg): time.sleep(3) print(arg) print('Start execution t1') t1 = threading.Thread(target=func,args=(3,)) t1.start() # No arguments: Let the main thread wait until the child thread t1 finishes executing before executing down # Arguments: Let the main thread wait here for up to n seconds, and continue down whether execution is complete or not t1.join(2) print('Start execution t2') t2 = threading.Thread(target=func,args=(9,)) t2.start() t2.join() # Let the main thread wait until the child thread t1 finishes executing before executing down print('end')
- Thread name acquisition
import threading def func(arg): # Gets the object of the thread currently executing the function t = threading.current_thread() # Gets the current thread name based on the current thread object name = t.getName() print(name,arg) t1 = threading.Thread(target=func,args=(3,)) t1.setName('mhy') t1.start() t2 = threading.Thread(target=func,args=(9,)) t2.setName('zz') t2.start() print('end')
- Thread nature
# Print 3 or end first? import threading def func(arg): print(arg) t1 = threading.Thread(target=func,args=(3,)) # Is start a thread?No # start tells the cpu that I'm ready and you can schedule me. t1.start() print('end')
- Object-oriented submultithreaded use
import threading class MyThread(threading.Thread): def run(self): print(123,self._args,self._kwargs) t1 = MyThread(args=(11,)) t1.start() t2 = MyThread(args=(12,)) t2.start()
- Computing-intensive multithreading (less useful)
import threading def func(lis,num): et = [i+num for i in lis] print(et) t1 = threading.Thread(target=func,args=([11,22,33],1)) t1.start() t2 = threading.Thread(target=func,args=([44,55,66],100)) t2.start()
- Instance Code
import threading import time #Definition class inherits Thread thread class CodingThread(threading.Thread): def run(self): for x in range(3): print('Writing code%s' % threading.current_thread()) time.sleep(1) class DrawingThread(threading.Thread): def run(self): for x in range(3): print('Drawing%s' % threading.current_thread()) time.sleep(1) def main(): t1 = CodingThread() t2 = DrawingThread() t1.start() t2.start() if __name__ == '__main__': main()
Thread Security (Lock)
- Thread-safe, internal queuing for all threads when multithreaded, such as list/dict/Queue
- Thread Insecurity + People (LOCK) = Queue Processing
Introduction:
The interpreter that comes with Python is CPython.The multithreading of the CPython interpreter is actually a pseudo multithreading (in a multicore CPU, only one core can be used, not multicore).There is only one thread executing at the same time. To ensure that only one thread is executing at the same time, there is something called a GIL (Global Intepreter Lock) in the CPython interpreter, called a global interpreter lock.This interpreter lock is necessary.Because memory management for the CPython interpreter is not thread safe.Of course, there are other interpreters besides the CPython interpreter, some of which do not have GIL locks. See below:
- Jython: A Python interpreter implemented in Java.There is no GIL lock.
- IronPython: A Python interpreter implemented with.net.There is no GIL lock.
- PyPy: A Python interpreter implemented in Python.There is a GIL lock.
The GIL is a pseudo multithreaded one.However, some IO operations, such as file reading and writing and network requests, can greatly improve efficiency.Multithreading is recommended for IO operations to improve efficiency.Multithreading is not recommended for some CPU computation operations, but multiprocessing is recommended.
Insecure examples
import time import threading lit = [] def func(arg): lit.append(arg) time.sleep(0.04) m = lit[-1] print(arg,m) # m and arg should be a value for num in range(10): t1 = threading.Thread(target=func,args=(num,)) t1.start()
Lock mechanism principle
Multiple threads execute simultaneously, which results in simultaneous execution of data and inconsistent results.
When a thread gets execute right first, it will lock the execution process, other threads can't use it. It must wait until the thread finishes execution before other threads can get execute right. Executing threads will solve the problem of data inconsistency
Lock Lock (one at a time)
import time import threading lit = [] lock = threading.Lock() # Create a lock def func(arg): lock.acquire() # Lock the line of code below until release is encountered lit.append(arg) time.sleep(0.04) m = lit[-1] print(arg,m) lock.release() # Release lock for num in range(10): t1 = threading.Thread(target=func,args=(num,)) t1.start()
RLock recursive locks (release more than one at a time),
Because lock locks can cause deadlocks if they have a multilevel lock mechanism, they have Rlock (recursive lock)
The code is as follows:
import time import threading lit = [] lock = threading.RLock() # Create a recursive lock def func(arg): # Lock can only unlock one lock, causing deadlock threads # RLock can unlock both locks, resolving the following issues lock.acquire() # Lock the line of code below until release is encountered lock.acquire() lit.append(arg) time.sleep(0.04) m = lit[-1] print(arg,m) lock.release() # Release lock lock.release() # Release lock for num in range(10): t1 = threading.Thread(target=func,args=(num,)) t1.start()
BoundedSemaphore (N at a time) semaphores
import time import threading # Create a lock that allows you to lock several times at a time, defaulting to one lock = threading.BoundedSemaphore(3) def func(arg): lock.acquire() print(arg) time.sleep(1) lock.release() for num in range(20): t = threading.Thread(target=func,args=(num,)) t.start()
Condition (x playback times) dynamic input
The Lock version of the Producer and Consumer model works fine, but there is a disadvantage. Among consumers, there is always a While True Dead Loop and Lock to determine that money is not enough. Locking is a CPU-intensive activity, so it is not the best way to do this, and a better way isUsing Threading.conditioning, threading.conditioning can be blocked waiting when there is no data, and notify-related functions can be used to notify other threads that are waiting once appropriate data is available.This eliminates the need for some useless locking and unlocking operations.Can improve program performance.First, the threading.Condition-related functions are introduced. Theading.Condition, like threading.Lock, can be locked when modifying global data or unlocked after modification.Here is a brief introduction to some of the commonly used functions:
- acquire: lock
- release: unlock
- Wait: Leaves the current thread waiting and releases the lock.It can be waked up by other threads using the notify and notify_all functions.Continue to wait for lock after waking up, and execute the following code after lock.
- notify: notifies a waiting thread, defaulting to the first waiting thread.
- Notify_all: Notifies the waiting thread.Notfy and notify_all do not release locks.And needs to be called before release.
# Method One import time import threading # Create a lock that allows you to lock several times at a time, defaulting to one lock = threading.Condition() def func(arg): print('Threads come in') lock.acquire() lock.wait() print(arg) time.sleep(1) lock.release() for num in range(3): t = threading.Thread(target=func,args=(num,)) t.start() while True: num = int(input('>>>:')) lock.acquire() lock.notify(num) lock.release() # Method 2 import time import threading # Create a lock that allows you to lock several times at a time, defaulting to one lock = threading.Condition() def xxx(): print('Execute the function') input('>>>:') return True def func(arg): print('Threads come in') lock.wait_for(xxx) print(arg) time.sleep(1) for num in range(3): t = threading.Thread(target=func,args=(num,)) t.start()
Event (event) put all at once
import threading # Create a lock that allows you to lock several times at a time, defaulting to one lock = threading.Event() def func(arg): print('Threads come in') lock.wait() # Red light print(arg) for num in range(10): t = threading.Thread(target=func,args=(num,)) t.start() input('>>>') lock.set() # Green light input('>>>') # Return to red light lock.clear() for num in range(10): t = threading.Thread(target=func,args=(num,)) t.start() input('>>>') lock.set() # Green light input('>>>')
Thread Summary
Thread Safety: Lists and dictionaries are thread safe;
Why lock?
- Non-Thread Security
- When controlling a piece of code, you can only execute at most a few simultaneous executions at a time
threading.local
Create a dictionary key-value pair for each thread to isolate data
Sample code:
import time import threading pond = threading.local() def func(arg): # Internally, a space is created for the current thread to store, phone = its own value, isolating the data pond.phone = arg time.sleep(2) # Value current thread's own space print(pond.phone,arg) for num in range(10): t = threading.Thread(target=func,args=(num,)) t.start()
Thread Pool
Use concurrent to create thread pools. Thread pools can effectively solve the endless problem of threads. Users create a thread with every request. Thread pools can help us solve this problem. Thread pools can be set, at most simultaneous.Line n threads, set by yourself.
Sample code:
import time from concurrent.futures import ThreadPoolExecutor # Create a thread pool (up to 5 threads executing simultaneously) pool = ThreadPoolExecutor(5) def func(arg1,arg2): time.sleep(1) print(arg1,arg2) for num in range(5): # De-thread pool requests a thread to execute func functions pool.submit(func,num,8)
Queue Thread Security Queue:
Locking is a frequent process when accessing global variables in a thread.If you want to store some data in a queue, Python has a built-in thread-safe module called the queue module.The queue module in Python provides synchronous, thread-safe queue classes, including FIFO (first in, first out) queue Queue and LIFO (last in, first out) queue LifoQueue.These queues implement the lock primitive, which can be interpreted as an atomic operation, either not doing it or doing it all, and can be used directly in multiple threads.Queues can be used to synchronize threads.The related functions are as follows:
- Initialize Queue(maxsize): Create a first-in-first-out queue.
- qsize(): Returns the size of the queue.
- empty(): Determines if the queue is empty.
- full(): Determine if the queue is full.
- get(): Take the last data from the queue.
- put(): Put a data into a queue.
Sample Code
#encoding:utf-8 from queue import Queue import threading import time # q = Queue(4) # Add 4 Queues # q.put(10) #The first queue inserts a piece of data # q.put(4) #The second queue inserts a piece of data # for x in range(4): # q.put(x) # # for x in range(4): # print(q.get()) # print(q.empty()) # print(q.full()) # print(q.qsize()) def set_value(q): index = 0 while True: q.put(index) index +=1 time.sleep(2) def get_value(q): while True: print(q.get()) def main(): q = Queue(4) t1 = threading.Thread(target=set_value,args=[q]) t2 = threading.Thread(target=get_value,args=[q]) t1.start() t2.start() if __name__ == '__main__': main()
Producer-consumer model
Model Three Parts
- Producer
- Queue: FIFO
- Stack: LIFO
- Consumer
- queue
The producer and consumer models solve the problem of not waiting all the time
Create Queue
Sample code:
import time import threading from queue import Queue q = Queue() def producer(id): '''Producer''' while True: time.sleep(2) q.put('Steamed stuffed bun') print('cook %s A bun was made'%id) def consumer(id): '''Consumer''' while True: time.sleep(1) v1 = q.get() print('customer %s I ate a bun'%id) for produce in range(1,4): t1 = threading.Thread(target=producer,args=(produce,)) t1.start() for consu in range(1,3): t2 = threading.Thread(target=consumer,args=(consu,)) t2.start()
Summary:
- Operating Systems Help Developers Operate Hardware
- Programmers write code to run on the operating system (relying on interpreters)
- There are especially many tasks.Previous execution, (serial), can now use multithreading
Why create threads?
- Since threads are the smallest unit of cpu work, creating threads can take advantage of multicore advantages to implement parallel operations (java,C#)
Why create a process?
- Data isolation between processes (java/C)
Python
- There is a GIL lock in Python.
- Cause: Multi-threading cannot take advantage of multi-core advantages
- Solution: Multiprocessing (wasting resources)
- Summary:
- IO intensive: multi-threaded
- Computing intensive: multi-process
- Thread Creation
- Thread
- Object-oriented inheritance (Threading.Thread)
- Other
- jojn
- setDeanon
- setName
- threading.current_thread()
- lock
- Get
- release