之前已经了解了进程的概念,程序并不能单独运行,只有将程序装载到内存中,系统为它分配资源才能运行
而这种执行的程序称为进程
程序和进程的区别在于:程序是指令的集合,它是进程运行的静态描述文本
为什么要有线程
进程有很多优点,它提供了多道编程,让我们感觉每个人都拥有自己的cpu和其他资源,可以提高计算机的利用率
进程只能在一个时间干一件事情,如果想干两件事情或多件事,进程就无能为力了。
进程在执行的过程中如果阻塞,例如等待输入,整个过程就会挂起,即使进程中有些工作不依赖输入的数据,也将
无法执行
进程和线程的关系
地址空间和其他资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其他进程不可见
通信:进程间通信IPC,线程间可以直接读写进程数据段(如全局变量)来进行通信----需要进程同步和互斥手段的辅助,以保证数据的一致性。
需要进程同步和互斥手段的辅助,以保证数据的一致性。
在多线程操作系统中,进程不是一个可执行的实体。
线程的特点
在多线程的操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
TCB包括以下信息: (1)线程状态。 (2)当线程不运行时,被保存的现场资源。 (3)一组执行堆栈。 (4)存放每个线程的局部变量主存区。 (5)访问同一个进程中的主存和其它资源。 用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
全局解释器锁GIL
Python代码的执行由Python虚拟机(也叫解释器主循环)来控制。Python在设计之初就考虑到要在主循环中,同时只有一个线程在执行。虽然 Python 解释器中可以“运行”多个线程,但在任意时刻只有一个线程在解释器中运行。
对Python虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。
在多线程环境中,Python 虚拟机按以下方式执行:
a、设置 GIL;
b、切换到一个线程去运行;
c、运行指定数量的字节码指令或者线程主动让出控制(可以调用 time.sleep(0));
d、把线程设置为睡眠状态;
e、解锁 GIL;
d、再次重复以上所有步骤。
在调用外部代码(如 C/C++扩展函数)的时候,GIL将会被锁定,直到这个函数结束为止(由于在这期间没有Python的字节码被运行,所以不会做线程切换)编写扩展的程序员可以主动解锁GIL。
python线程模块的选择
Python提供了几个用于多线程编程的模块,包括thread、threading和Queue等。thread和threading模块允许程序员创建和管理线程。thread模块提供了基本的线程和锁的支持,threading提供了更高级别、功能更强的线程管理的功能。Queue模块允许用户创建一个可以用于多个线程之间共享数据的队列数据结构。
避免使用thread模块,因为更高级别的threading模块更为先进,对线程的支持更为完善,而且使用thread模块里的属性有可能会与threading出现冲突;其次低级别的thread模块的同步原语很少(实际上只有一个),而threading模块则有很多;再者,thread模块中当主线程结束时,所有的线程都会被强制结束掉,没有警告也不会有正常的清除工作,至少threading模块能确保重要的子线程退出后进程才退出。
thread模块不支持守护线程,当主线程退出时,所有的子线程不论它们是否还在工作,都会被强行退出。而threading模块支持守护线程,守护线程一般是一个等待客户请求的服务器,如果没有客户提出请求它就在那等着,如果设定一个线程为守护线程,就表示这个线程是不重要的,在进程退出的时候,不用等待这个线程退出。
threading模块
multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍
线程的创建
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
vfrom threading import Thread import time import os def sayhi(name): time.sleep(2) print('%s say hello'%name,os.getpid()) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() print('主线程',os.getpid())
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import time class Sayhi(Thread): def __init__(self,name): super().__init__() self.name=name def run(self): time.sleep(2) print('%s say hello' % self.name) if __name__ == '__main__': t = Sayhi('egon') t.start() print('主线程')
多线程和多进程的比较
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread from multiprocessing import Process import os def work(): print('hello',os.getpid()) if __name__ == '__main__': # part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样 t1=Thread(target=work) t2=Thread(target=work) t1.start() t2.start() print('主线程/主进程',os.getpid()) # part2:开多个进程,每个进程都有不同的pid p1=Process(target=work) p2=Process(target=work) p1.start() p2.start() print('主线程/主进程',os.getpid()) ''' hello 8936 hello 8936 主线程/主进程 8936 主线程/主进程 8936 hello 12400 hello 19004 '''
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread from multiprocessing import Process import os def work(): print('hello') if __name__ == '__main__': # 在主进程下开启线程 t=Thread(target=work) t.start() print('主线程/主进程') print('-----------------------') # 在主进程下开启子进程 t=Process(target=work) t.start() print('主线程/主进程') ''' hello 主线程/主进程 ----------------------- 主线程/主进程 hello '''
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
vfrom threading import Thread from multiprocessing import Process import os def work(): global n n=0 if __name__ == '__main__': # n=100 # p=Process(target=work) # p.start() # p.join() # print('主',n) #主 100 n=100 t=Thread(target=work) t.start() t.join() print('主',n) #主 0
练习:多线程实现socket
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import multiprocessing import threading import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(('127.0.0.1',8080)) s.listen(5) def action(conn): while True: data=conn.recv(1024) print(data) conn.send(data.upper()) if __name__ == '__main__': while True: conn,addr=s.accept() p=threading.Thread(target=action,args=(conn,)) p.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect(('127.0.0.1',8080)) while True: msg=input('>>: ').strip() if not msg:continue s.send(msg.encode('utf-8')) data=s.recv(1024) print(data)
Thread类的其他方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Thread实例对象的方法 # isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名。 threading模块提供的一些方法: # threading.currentThread(): 返回当前的线程变量。 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
例子:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import threading from multiprocessing import Process import os def work(): import time time.sleep(3) print(threading.current_thread().getName()) #Thread-1 if __name__ == '__main__': t=Thread(target=work) t.start() print(threading.current_thread().getName()) #MainThread print(threading.current_thread()) # <_MainThread(MainThread, started 11068)> print(threading.enumerate()) #[<_MainThread(MainThread, started 11068)>, <Thread(Thread-1, started 11436)>] print(threading.active_count()) # 2 print('主线程/主进程')
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello'%name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.start() # t.join() print('主线程') print(t.is_alive()) ''' 有t.join()的情况 egon say hello 主线程 False 没有t.join()的情况 主线程 True egon say hello '''
守护线程
无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。需要强调的是:运行完毕并非终止运行
#1.对主进程来说,运行完毕指的是主进程代码运行完毕 #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
#1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束, #2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import time def sayhi(name): time.sleep(2) print('%s say hello' %name) if __name__ == '__main__': t=Thread(target=sayhi,args=('egon',)) t.setDaemon(True) #必须在t.start()之前设置 t.start() print('主线程') print(t.is_alive()) ''' 主线程 True #由于t设置为守护线程,所以等到主线程完毕了,子线程来不及打印就结束了 '''
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import time def foo(): print(123) time.sleep(1) print("end123") def bar(): print(456) time.sleep(3) print("end456") t1=Thread(target=bar) t2=Thread(target=foo) t1.daemon=True t1.start() t2.start() print("main-------") ''' 456 123 main------- end123 '''
线程的执行,是异步的,顺序随机
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import os import time from threading import Thread def func(i): print('--->子线程', os.getpid()) time.sleep(1) print('*' * i) if __name__ == '__main__': print('主进程', os.getpid()) start = time.time() thread_lst = [] for i in range(10): t = Thread(target=func, args=(i,)) t.start() # Thread.name 获取和设置线程的名称 # Thread.ident 获取线程的标识符 print('-->', t.name, t.ident) thread_lst.append(t) for t in thread_lst: t.join() # 等待所有线程结束 print(time.time() - start) ''' 主进程 10816 --->子线程 10816 --> Thread-1 16904 --->子线程 10816 --> Thread-2 3112 --->子线程 10816 --> Thread-3 16256 --->子线程 10816 --> Thread-4 960 --->子线程 10816 --> Thread-5 896 --->子线程 10816 --> Thread-6 18740 --->子线程 10816 --> Thread-7 17364 --->子线程 10816 --> Thread-8 15868 --->子线程 10816 --> Thread-9 5948 --->子线程 10816 --> Thread-10 9356 ****** ** ********* * *** ******** ******* **** ***** 1.0063774585723877 '''
锁
多个线程或者多个进程操作同一个数据,容易造成数据不安全,所以需要加把锁
GIL锁的是线程,不能锁住内存中的数据,对于全局变量的修改必须要枷锁,为了不影响效率,不是共享的数据不用设置成全局变量
资源的抢占例子一:sleep的值为0.1的情况
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread import os,time def work(): global n temp=n print('---','temp:%s ,n:%s'%(id(temp),id(n))) print(temp) time.sleep(0.1) # 0.01 0.001 0.0001 n=temp-1 if __name__ == '__main__': n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() print('----主-----',n)
改变sleep的值,得到的结果显示如下
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
--- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 ----主----- 99
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
--- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 --- temp:1961589216 ,n:1961589216 97 97 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 --- temp:1961589184 ,n:1961589184 96 96 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 --- temp:1961588992 ,n:1961588992 90 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 --- temp:1961588960 ,n:1961588960 89 ----主----- 88
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
--- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589312 ,n:1961589312 100 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589280 ,n:1961589280 99 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589248 ,n:1961589248 98 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589216 ,n:1961589216 97 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589184 ,n:1961589184 96 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589152 ,n:1961589152 95 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589120 ,n:1961589120 94 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589088 ,n:1961589088 93 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589056 ,n:1961589056 92 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961589024 ,n:1961589024 91 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 --- temp:1961588992 ,n:1961588992 90 ----主----- 89
加锁的情况
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
分析: #1.100个线程去抢GIL锁,即抢执行权限 #2. 肯定有一个线程先抢到GIL(暂且称为线程1),然后开始执行,一旦执行就会拿到lock.acquire() #3. 极有可能线程1还未运行完毕,就有另外一个线程2抢到GIL,然后开始运行,但线程2发现互斥锁lock还未被线程1释放,于是阻塞,被迫交出执行权限,即释放GIL #4.直到线程1重新抢到GIL,开始从上次暂停的位置继续执行,直到正常释放互斥锁lock,然后其他的线程再重复2 3 4的过程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from threading import Thread,Lock import os,time def work(): global n lock.acquire() temp=n time.sleep(0.1) #此处的sleep无论取什么值,或者注销加锁都是一样的值 n=temp-1 lock.release() if __name__ == '__main__': lock=Lock() n=100 l=[] for i in range(100): p=Thread(target=work) l.append(p) p.start() for p in l: p.join() print(n) #结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#不加锁:并发执行,速度快,数据不安全 from threading import current_thread,Thread,Lock import os,time def task(): global n print('%s is running' %current_thread().getName()) temp=n time.sleep(0.5) n=temp-1 if __name__ == '__main__': n=100 lock=Lock() threads=[] start_time=time.time() for i in range(100): t=Thread(target=task) threads.append(t) t.start() for t in threads: t.join() stop_time=time.time() print('主:%s n:%s' %(stop_time-start_time,n)) ''' Thread-1 is running Thread-2 is running ...... Thread-100 is running 主:0.5216062068939209 n:99 ''' #不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全 from threading import current_thread,Thread,Lock import os,time def task(): #未加锁的代码并发运行 time.sleep(3) print('%s start to run' %current_thread().getName()) 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(100): t=Thread(target=task) threads.append(t) t.start() for t in threads: t.join() stop_time=time.time() print('主:%s n:%s' %(stop_time-start_time,n)) ''' Thread-1 is running Thread-2 is running ...... Thread-100 is running 主:53.294203758239746 n:0 ''' #有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊 #没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是 #start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的 #单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高. from threading import current_thread,Thread,Lock import os,time def task(): time.sleep(3) print('%s start to run' %current_thread().getName()) global n temp=n time.sleep(0.5) n=temp-1 if __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('主:%s n:%s' %(stop_time-start_time,n)) ''' Thread-1 start to run Thread-2 start to run ...... Thread-100 start to run 主:350.6937336921692 n:0 #耗时是多么的恐怖 '''
加锁不当也会出现死锁的情况
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time from threading import Lock,Thread noodle = 100 fork = 100 noodle_lock = Lock() fork_lock = Lock() def eat1(name): global noodle,fork noodle_lock.acquire() print('%s拿到面了' % name) fork_lock.acquire() print('%s拿到叉子了' % name) noodle -= 1 print('%s吃面'%name) time.sleep(0.1) fork_lock.release() print('%s放下叉子了' % name) noodle_lock.release() print('%s放下面' % name) def eat2(name): global noodle,fork fork_lock.acquire() print('%s拿到叉子了'%name) noodle_lock.acquire() print('%s拿到面了'%name) noodle -= 1 print('%s吃面'%name) time.sleep(0.1) noodle_lock.release() print('%s放下面'%name) fork_lock.release() print('%s放下叉子了'%name) for i in ['alex','wusir','egon','快老师']: Thread(target=eat1,args=(i,)).start() Thread(target=eat2,args=(i+'2',)).start() #结果夯住
进而引入了递归锁
# 递归锁的特点 # 如果能够在第一个acquire的地方通过 # 那么在一个线程中后面所有的acquire都能通过 # 但是其他所有的线程都会在第一个acquire处阻塞 # 在这个线程中acquire了多少次,就必须release多少次 # 如果acquire的次数和release的次数不想等,那么其他线程也不能继续向下执行
典型例子:科学家吃面
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time from threading import Thread,Lock noodle_lock = Lock() fork_lock = Lock() def eat1(name): noodle_lock.acquire() print('%s 抢到了面条'%name) fork_lock.acquire() print('%s 抢到了叉子'%name) print('%s 吃面'%name) fork_lock.release() noodle_lock.release() def eat2(name): fork_lock.acquire() print('%s 抢到了叉子' % name) time.sleep(1) noodle_lock.acquire() print('%s 抢到了面条' % name) print('%s 吃面' % name) noodle_lock.release() fork_lock.release() for name in ['哪吒','egon','yuan']: t1 = Thread(target=eat1,args=(name,)) t2 = Thread(target=eat2,args=(name,)) t1.start() t2.start()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import time from threading import Thread,RLock fork_lock = noodle_lock = RLock() def eat1(name): noodle_lock.acquire() print('%s 抢到了面条'%name) fork_lock.acquire() print('%s 抢到了叉子'%name) print('%s 吃面'%name) fork_lock.release() noodle_lock.release() def eat2(name): fork_lock.acquire() print('%s 抢到了叉子' % name) time.sleep(1) noodle_lock.acquire() print('%s 抢到了面条' % name) print('%s 吃面' % name) noodle_lock.release() fork_lock.release() for name in ['哪吒','egon','yuan']: t1 = Thread(target=eat1,args=(name,)) t2 = Thread(target=eat2,args=(name,)) t1.start() t2.start()
线程队列 ----queue
queue是线程的队列,用法和进程的Queue类似
1 queue.Queue 先进先出
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import queue q=queue.Queue() q.put('first') q.put('second') q.put('third') print(q.get()) print(q.get()) print(q.get()) ''' 结果(先进先出): first second third '''
2 queue.LifoQueue 后进先出
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import queue q=queue.LifoQueue() q.put('first') q.put('second') q.put('third') print(q.get()) print(q.get()) print(q.get()) ''' 结果(后进先出): third second first '''
3 queue.PriorityQueue 优先级队列,数字越小,优先级越高,越先取出
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import queue q=queue.PriorityQueue() #put进入一个元组,元组的第一个元素是优先级(通常是数字,也可以是非数字之间的比较),数字越小优先级越高 q.put((20,'a')) q.put((10,'b')) q.put((30,'c')) print(q.get()) print(q.get()) print(q.get()) ''' 结果(数字越小优先级越高,优先级高的优先出队): (10, 'b') (20, 'a') (30, 'c') '''