python 线程 进程与线程 GIL: 计算密集型与I/O密集型 线程锁 信号量锁: enevt同步
一颗cpu同一时刻只能有一个线程执行。在cpython解释器中,同一时刻只能让同一个进程中只有一个线程进入解释器
GIL是内核级别的锁:为了保护内核,被多线程干扰
线程锁是用户级别的锁:为了保护程序里面的数据
线程:
是最小的执行单位,是一堆指令的集合。cpu会记录上下文关系
数据之间是共享的
一个进程至少有一个线程
进程:
进程是不修改数据的。
一堆资源(线程、cpu、内存等)的管理的集合
进程之间的数据是不共享的。
GIL:
在cpython解释器中,同一时刻只能让同一个进程中只有一个线程进入解释器。因为同一进程中的线程是共享数据的
在开发这么语言的时候,只有一颗cpu,为了保护在多线程保护数据一致性,所以开发了GIL。去锁有大咖尝试做过,但是效率更低
In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once.
This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists,
other features have grown to depend on the guarantees that it enforces.)
计算密集型与I/O密集型
io密集型:
线程在遇到I/0(sock的收发,文件的读写,sleep等)的时候,会自动进行切换到另外一个线程计算。就算只有一颗cpu的情况下,也大量节省了时间。
如下代码,多线程会比串行节约一倍的时间
import time import threading def foo1(): time.sleep(8) print("1") def foo2(): time.sleep(8) print("2") start=time.time() # foo1() # foo2() a=threading.Thread(target=foo1) b=threading.Thread(target=foo2) a.start() b.start() a.join() b.join() end=time.time() print("run time:",end-start)
计算密集型:
下列代码,就是io密集型的表现,使用串行算法与 多线程算法 两者的时间几乎没区别(在python2.7和3.4中,多线程io因为不断切换线程,时间比串行慢了一半。3.5已经优化) python中因为GIL的存在,线程只能在一个cpu中计算。对应计算密集型多线程 不适用
import time import threading def add(n): sum = 0 for i in range(n): sum += i print(sum) start = time.time() # add(100000000) # add(200000000) a=threading.Thread(target=add,args=(100000000,)) b=threading.Thread(target=add,args=(200000000,)) a.start() b.start() a.join() b.join() end = time.time() print("time:",start-end)
解决同一个进程中的多线程在同一时刻只能有一个进入cpu的问题,可以利用多协程与携程 等等解决方法。
线程的另外一种调用方式:
import threading import time class MyThread(threading.Thread): def __init__(self,num): threading.Thread.__init__(self) self.num = num def run(self):#定义每个线程要运行的函数 print("running on number:%s" %self.num) time.sleep(3) if __name__ == '__main__': t1 = MyThread(1) t2 = MyThread(2) t1.start() t2.start()
jion方法:
阻塞线程,等待线程执行完毕。 线程可以通过列表装载。
setdeamon:
守护进程,主进程死了,下面的线程全挂、
线程锁
是为了解决线程之间 操作共享数据的问题
线程不安全:
如下代码:线程之间共享数据,假如第一次一个线程拿到全局变量,进行计算的时候,到了cpu的执行时间,不会进行最后一步重新赋值全局变量。
但是第二次线程拿到全局变量,执行完毕,赋值给全局变量。两次的执行效果都是一样。
import threading,time def add(): global num temp = num temp -= 1 time.sleep(0.001) num = temp num = 200 thread_list=[] for i in range(100): t = threading.Thread(target=add,args=()) t.start() thread_list.append(t) for t in thread_list: t.join() print(num)
解决方案:线程锁,就是加在线程上,告诉解释器,我必须执行完,你才能给我进行切换!
acquire 与 release 之间变成了串行,就算遇到I/0也会被锁住
但是在该线程 锁之外的东西(如下print(ok))还是可以进行cpu切换的,
这就是和join的区别。 join是把线程所有的部分都变成了串行
import threading,time def add(): global num #!!acquire 与 release 之间变成了串行,就算遇到I/0也会被锁住 # 但是在该线程 锁之外的东西(如下print(ok))还是可以进行cpu切换的, # 这就是和join的区别。 join是把线程所有的部分都变成了串行 print("ok") r.acquire() temp = num temp -= 1 time.sleep(0.001) num = temp r.release() num = 200 thread_list=[] r = threading.Lock() for i in range(100): t = threading.Thread(target=add,args=()) t.start() thread_list.append(t) for t in thread_list: t.join() print(num)
GIL 是 一个进程的所有线程,同一时刻只能一个进入cpu
线程锁 是保证 一个进程中,多线程操作共享数据的时候,为了避免到时 切换cpu而操作数据的不安全。
递归锁:普通的锁 只能被使用一次, 相互等待锁的时候,就会被锁死。(如下代码)递归锁 维护了 一个 计数器与对应列表。
import threading,time class myThread(threading.Thread): def doA(self): lockA.acquire() print(self.name,"gotlockA",time.ctime()) time.sleep(3) lockB.acquire() print(self.name,"gotlockB",time.ctime()) lockB.release() lockA.release() def doB(self): lockB.acquire() print(self.name,"gotlockB",time.ctime()) time.sleep(2) lockA.acquire() print(self.name,"gotlockA",time.ctime()) lockA.release() lockB.release() def run(self): self.doA() self.doB() if __name__=="__main__": lockA=threading.Lock() lockB=threading.Lock() threads=[] for i in range(5): threads.append(myThread()) for t in threads: t.start() for t in threads: t.join()#等待线程结束,后面再讲。
递归锁在类中包含 账户 转账、取现的时候,每个方法都加锁,同时一个方法调用前面函数,就需要用到递归锁了
信号量锁:
同一时刻只有三个链接并发。
r = threading.BoundedSemaphore(3)
r.acqure()
r.release()
条件同步锁
有一类线程需要满足条件之后才能够继续执行,Python提供了threading.Condition 对象用于条件变量线程的支持,
它除了能提供RLock()或Lock()的方法外,还提供了 wait()、notify()、notifyAll()方法。
lock_con=threading.Condition([Lock/Rlock]): 锁是可选选项,不传人锁,对象自动创建一个RLock()。
wait():条件不满足时调用,线程会释放锁并进入等待阻塞;等到了notify通知时候,会从acquire从新执行
notify():条件创造后调用,通知等待池激活一个线程;
notifyAll():条件创造后调用,通知等待池激活所有线程。
enevt同步
这个不是锁了,但是效果类似条件同步锁
创建的时候,默认是Flase flag=thread.Event()
event.isSet():返回event的状态值; event.wait():如果 event.isSet()==False将阻塞线程;当set的时候,立马执行 event.set(): 设置event的状态值为True,所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; event.clear():恢复event的状态值为False。
import threading,time import random def light(): if not event.isSet(): event.set() #wait就不阻塞 #绿灯状态 count = 0 while True: if count < 10: print('