Introduction to a multi-threading concept
Threading Module Introduction
The threading module and the multiprocessing module have great similarity in the use level.
Second, the two ways to open multithreading
Creating a thread is less expensive than creating a process, so it is faster to create a thread.
#开启进程的第一种方式from multiprocessing Import processfrom threading import Threadimport osimport timedef work (): print (' <%s> is running '%os.getpid ()) Time.sleep (2) print (' <%s> was done '%os.getpid ()) If __name__ = = ' __ Main__ ': t=thread (Target=work,) # t= Process (target=work,) t.start () print (' main ', Os.getpid ())
#开启线程的第二种方式 (with Class) from threading import Threadimport timeclass work (Thread): def __init__ (self,name): super (). _ _init__ () self.name = name def run (self): # Time.sleep (2) print ('%s say hell '%self.name) if __name__ = = ' __main__ ': t = Work (' Egon ') T.start () print (' main ')
The difference between opening multiple threads under one process and opening multiple sub-processes under one process
#线程的开启速度大于进程的开启速度from multiprocessing Import processfrom threading Import Threadimport timedef work (): Time.sleep (2) print (' hello ') if __name__ = = ' __main__ ': t = Thread (target=work) #如果等上几秒, he will print the master first in the process of opening, If no wait will be printed first Hello # t = Process (target=work) #子进程会先打印主, t.start () print (' master ')
#在同一个进程下开多个进程和开多个线程的pid的不同 # 2.----------from multiprocessing import processfrom threading Import Threadimport Osdef work (): print (' Hello ', os.getpid ()) If __name__ = = ' __main__ ': #在主进程下开启多个线程, each thread is the same as the PID of the main process t1= Thread (target=work) t2 = thread (target=work) T1.start () t2.start () print (' main thread pid ', Os.getpid ()) #来多个进程, each process has a different PID p1 = Process (target=work) P2 = Process (target=work) P1.start () P2.start () print (' main process pid ', os.getpid ())
#同一进程内的线程共享该进程的数据from Threading Import threadfrom multiprocessing import processimport osdef work (): Global n n-=1 print (n) #所以被改成99了n = 100if __name__ = = ' __main__ ': # p = Process (target=work) p = Thread (target=work) #当开启的是线程的时候 because the in-process data is shared between threads within the same process #所以打印的n为99 p.start () p.join () Print (' main ', N) #毫无疑问子进程p已经将自己的全局的n改成了0, # But instead of just its own, check that the parent process's n is still 100
Processes are isolated from each other and are not shared. Need a third party to complete the share (with queues, pipelines, shared data)
Third, practice
Practice one: Multithreading to implement concurrency
#服务端from Socket Import *from Threading Import Threads = socket (af_inet,sock_stream) s.setsockopt (sol_socket,so_ reuseaddr,1) #端口重用s. Bind ((' 127.0.0.1 ', 8081)) S.listen (5) Print (' Start running ... ') def talk (COON,ADDR): While True: # Communication Loop try: cmd = coon.recv (1024x768) print (Cmd.decode (' Utf-8 ')) if not cmd:break Coon.send (Cmd.upper ()) print (' Send%s '%cmd.upper (). Decode (' Utf-8 ')) except Exception: Break Coon.close () if __name__ = = ' __main__ ': While True: #链接循环 coon,addr = s.accept () print (COON,ADDR) P =thread (target=talk,args= (coon,addr)) P.start () s.close ()
#客户端from Socket Import *c = socket (af_inet,sock_stream) c.connect ((' 127.0.0.1 ', 8081)) while True: cmd = input (' > Strip () if not cmd:continue c.send (Cmd.encode (' Utf-8 ')) data = C.recv (1024x768) print (' Accept%s ') %data.decode (' Utf-8 ')) C.close ()
Practice 2:3 tasks, one to receive user input, one to format user input into uppercase, and one to save formatted results to a file
From threading import Threadimport osinput_l = []format_l = []def talk]: #监听输入任务 while True: cmd = input (' >> ;: '). Strip () if not cmd:continue input_l.append (CMD) def format (): While True: if input_l: res = Input_l.pop () #取出来 format_l.append (Res.upper ()) #取出来后变大写def Save (): While True: if format_l: # If format_l is not empty with open (' db ', ' a ') as F: F.write (Format_l.pop () + ' \ n ') #写进文件 f.flush () if __name__ = = ' __ Main__ ': t1=thread (Target=talk) t2=thread (Target=format) t3=thread (target=save) T1.start () T2.start () T3.start ()
Four, multi-thread sharing address space within the same process
From threading import threadfrom multiprocessing import processimport osn = 100def talk (): global n n-=100 PR Int (n) If __name__ = = ' __main__ ': t = Thread (target=talk) #如果开启的是线程的话, n=0 # t = Process (target=talk) #如果开启的是进程的话, n=100 T.start () t.join () print (' main ', N)
V. Other properties and methods of thread objects
thread instance object # IsAlive (): Returns whether the thread is active. # GetName (): Returns the thread name. # SetName (): Sets the thread name. threading module provides some methods: # Threading.currentthread (): Returns the current thread variable. # Threading.enumerate (): Returns a list that contains the running thread. Running refers to threads that do not include pre-and post-termination threads until after the thread has started and ends. # Threading.activecount (): Returns the number of running threads, Has the same result as Len (Threading.enumerate ()).
#线程的其他属性和方法from Threading Import threadfrom multiprocessing import processimport time,os,threadingdef work (): Time.sleep (2) print ('%s is running '% threading.currentthread (). GetName ()) print (Threading.current_ Thread ()) #其他线程 print (Threading.currentthread (). GetName ()) #得到其他线程的名字if __name__ = = ' __main__ ': t = Thread ( Target=work) T.start () print (Threading.current_thread (). GetName ()) #主线程的名字 Print ( Threading.current_thread ()) #主线程 print (Threading.enumerate ()) #连同主线程在内有两个运行的线程 time.sleep (2) Print (T.is_alive ()) #判断线程是否存活 print (Threading.activecount ()) print (' master ')
Vi. Join and Daemon threads
All non-daemon child processes, such as the main process, end before he ends (reclaim resources for its child processes): (with parent-child relationship)
The main thread and other non-daemon threads all end before it ends: (No parent-child relationship)
# joinfrom Threading Import Threadimport Time,osdef Talk (): time.sleep (3) print ('%s is running: '%o S.getpid ()) If __name__ = = ' __main__ ': t = Thread (target=talk) T.start () t.join () #主进程在等子进程结束 Print (' master ')
The difference between a daemon and a daemon
1. Daemon: The master process waits until all the non-daemons have ended before destroying the daemon. That is, (the main process ran out of the guardian of the one killed)
2. Daemon thread: The main thread has run out of the Guardian's that has not been killed, the main thread and other non-daemon threads are all finished before it ends
#守护进程和守护线程from multiprocessing Import processfrom threading Import Thread,currentthreadimport time,osdef talk1 (): Time.sleep (2) print (' Hello ') def talk2 (): time.sleep (2) print (' see ') if __name__ = = ' __ main__ ': t1 = thread (target=talk1) t2 = thread (TARGET=TALK2) # t1 = Process (target=talk1) # t2 = Process (TARGET=TALK2) T1.daemon = True t1.start () t2.start () print (' main thread ', Os.getpid ())
#----Confusing example----from threading import Threadimport timedef foo (): print (123) # Time.sleep (10) # If this time is greater than the time to wait, it will not print end123 time.sleep (2) #如果这个等的时间小于下面等的时间, the end123 also printed print (' end123 ') def Bar () : print (456) # Time.sleep (5) time.sleep print (' end456 ') if __name__ = = ' __main__ ': t1 = Thread (target=foo) t2 = thread (target=bar) T1.daemon = True #主线程运行完了守护的那个还没有干掉, # The main thread and other non-daemon threads all end before it ends T1.start () t2.start () print (' main---------')
Seven, Gil and lock
1.python GIL (Global interpreter Lock) #全局的解释器锁
2. Purpose of the Lock: sacrificing efficiency to ensure data security
3. Protect different data and different locks ()
4.python comes with garbage collection
5. Who gets the Gil lock, let who get the CPython interpreter's EXECUTE permission
6.GIT locks Protect the security of CPython interpreter data without protecting the data of your own programs
7.GIL Lock when it comes to blocking, it is forced to release the lock, then the other began to grab the lock, grab
After the bar value has been modified, but the first to get the data is still in the original to stay, when again take
When the lock, the data has been modified, and you take the original, so it is chaotic, so it is not guaranteed
The data is safe.
8. So how to solve the data security ne?
Add it yourself. Lock: Mutex=lock ()
Eight, synchronous lock
Gil and lock are two locks, the protection of the data is not the same, the former is the interpreter level (of course, the protection of the interpreter-level data, such as garbage collection data), the latter is to protect the user's own development of the application data, it is obvious that Gil is not responsible for this matter, only user-defined lock processing, namely lock
Process Analysis: All threads rob the Gil lock, or all threads rob the Execute permission
Thread 1 Grab the Gil Lock, get execution permissions, start execution, and then add a lock, not finished, that is, thread 1 has not released lock, it is possible that thread 2 grab the Gil Lock, start execution, the execution of the process found that lock has not been released by thread 1, and then thread 2 into the block, take the execution permissions , it is possible that thread 1 gets the Gil and then normal execution to release lock ... This leads to the effect of serial operation
Since it's serial, we're doing it.
T1.start ()
T1.join
T2.start ()
T2.join ()
This is also serial execution Ah, why add lock, it is necessary to know that the join is waiting for T1 all the code to complete, the equivalent of locking T1 all the code, and lock is only a part of the operation to share data sharing code.
Because the Python interpreter helps you automatically recycle memory periodically, you can understand that there is a separate thread in the Python interpreter, and each time it wake up to do a global poll to see which memory data can be emptied, the line in your own program Rountines accesses The PY interpreter's own thread is running concurrently, assuming that your thread has deleted a variable, and that the garbage collection thread of the PY interpreter is clearing in the process of emptying the variable, and maybe a different thread has just re-assigned the memory space that has not yet been emptied. As a result, it is possible that the newly-assigned data is deleted, and in order to solve a similar problem, the Python interpreter simply adds a brute lock, that is, when a thread is running, no one else can move, which solves the problem above, which can be said to be the legacy of earlier versions of Python.
# Global explanation Lock from threading import Thread,lockimport timen=100def work (): mutex.acquire () global n temp=n Time.sleep (0.01) n=temp-1 mutex.release () if __name__ = = ' __main__ ': mutex=lock () t_l=[] s= Time.time () for I in range (+): t=thread (target=work) t_l.append (t) T.start () for T in t_l : t.join () print ('%s:%s '% (Time.time ()-s,n))
Locks are often used to achieve synchronous access to shared resources. Create a lock object for each shared resource, and when you need to access the resource, call the Acquire method to get the lock object (if the other thread has already acquired the lock, the current thread waits for it to be freed), and then call the release method to release the lock when the resource has finished accessing it:
# The format of the lock import Threadingmutex = Threading. Lock () Mutex.aquire () "operation on public Data" ' Mutex.release ()
Analysis:
1.1 Threads to Rob Gil Lock, that is, to rob Execute permission 2. There must be a line enters upgradeable grab Gil (called thread 1), then start execution, and once executed you will get Lock.acquire () 3. It is very likely that thread 1 has not finished running, there is another thread 2 grab the Gil, and then start running, but thread 2 found that the mutex lock was not released by thread 1, then blocked, forced to surrender execution rights, that is, the release of Gil 4. Until thread 1 grabs the Gil and starts to resume from the last paused position, Until the mutex lock is normally released, then the other threads repeat the process of 2 3 4
If not locked: concurrent execution, fast, data insecure.
Locking: Serial execution, slow speed, data security.
# The difference between mutex and join (Focus!!!) #不加锁: Concurrent execution, fast, data insecure from threading import Current_thread,thread,lockimport os,timedef Task (): Global n Print ('%s ') Running '%current_thread (). GetName ()) Temp=n time.sleep (0.5) n=temp-1if __name__ = = ' __main__ ': n=100 lock= Lock () threads=[] Start_time=time.time () for I in range: T=thread (target=task) threads.append ( T) T.start () for T in Threads:t.join () Stop_time=time.time () print (' main:%s n:%s '% (stop_time-start_t Ime,n) "Thread-1 is RunningThread-2" is running ... THREAD-100 is running main: 0.5216062068939209 n:99 "#不加锁: Unlocked part concurrent execution, lock part serial execution, slow, data security from threading import Current_ Thread,thread,lockimport os,timedef Task (): #未加锁的代码并发运行 time.sleep (3) print ('%s start to run '%current_thread (). g Etname ()) Global n #加锁的代码串行运行 lock.acquire () temp=n time.sleep (0.5) n=temp-1 lock.release () if __name_ _ = = ' __main__ ': n=100 lock=lock () threads=[] Start_time=time.time ()For I in range: T=thread (target=task) threads.append (t) T.start () for T in Threads:t. Join () Stop_time=time.time () print (' main:%s n:%s '% (stop_time-start_time,n)) ' Thread-1 is RunningThread-2 is running. .... THREAD-100 is running main: 53.294203758239746 n:0 "#有的同学可能有疑问: Since the lock will make the run into serial, then I immediately after start to use join, do not have to lock Ah, is also a serial effect AH # Yes: Using jion immediately after start will certainly turn the execution of the 100 tasks into serial, without a doubt, the result of the final n is certainly 0, is safe, but the problem is join immediately after #start: all the code within the task is executed serially, and the lock, The only part of the lock that modifies the shared data is the serial # order from the data security aspect, both can be achieved, but it is obvious that the lock is more efficient. From threading import Current_thread,thread,lockimport OS, Timedef task (): Time.sleep (3) print ('%s start to run '%current_thread (). GetName ()) Global n temp=n time.sle EP (0.5) n=temp-1if __name__ = = ' __main__ ': n=100 lock=lock () start_time=time.time () for I in Range (100): T=thread (Target=task) T.start () T.join () Stop_time=time.time () print (' main:%s n:%s '% (Stop_time-star T_time,n) "Thread-1 start to runThread-2 start to run ... Thread-100 start to run Master: 350.6937336921692 n:0 #耗时是多么的恐怖 "
Python Full Stack Development Foundation "24th" (using threading module thread, join and daemon threads, Gil and lock)