一 线程的理论
什么是线程:线程就是开启一个进程,而执行进程里任务的就是线程。在一个进程里面可以创建多个线程,也叫做多线程。
每创建一个主进程,同时就会产生一个控制线程,来执行主进程里面的任务。而这个线程就叫做主线程。主线程是可以用了开辟其他的子线程的,分发多个子线程。子线程下面还可以再有子线程
在一个进程中创建的多个线程都可以共享这个进程里面的资源。
为什么线程的开销小:创建线程是不会产生单独的内存空间的,而是在他的进程里面直接就会产生。
线程和进程之间的关系
1 线程一定是寄托在进程里面的。
2一个进程可以分发多个线程,线程是最小的执行单位
3 进程和线程的切换,切换时操作系统操作的,
4切换的操作者:
4,1 时间片:就是切换的时候停止的时间
4,2 I/O操作切换:I/O卡住时,是不会占用CPU的。
4,3 优先级切换。
二 线程和进程的区别
区别1:在同一个进程下的多个线程是可以共享这个进程里面的资源的。
进程之间都会有独立的内存空间的,他们之间没有人任何关系的。
区别2:线程之间是可以直接访问他们共同进程下的数据并进行修改。
而进程之间的访问是需要依赖于ipc的。
区别3:线程的创建是不需要重新开启单独的内存空间的。
每一个进程的产生,就会产生一个内存空间的。
区别4:同一个进程下的多个线程之间是可以相互影响的
进程之间是没有任何影响的。
区别5:同一个进程下的多个线程修改数据,都会影响到其他线程的
进程之间,修改数据对其他的进程是没有任何影响的
三 为什么要用多线程
多个任务在一个进程里面完成,那就必须在这个进程里面开启多个线程。
1 多线程共享着一个进程的内存空间
2 线程比进程更轻量级,而线程比进程更加容易的创建和撤销。创建一个线程的速度会比创建一个进程的速度快上10到100倍之间。
3 如果多个线程都是CPU密集型时,并不能获得性能上的提升。拥有多线程允许任务之间互相重叠运行,从而加快程序的执行速度
4 为了利用多核,开启多个线程比开启多个进程的开销要小的多。在同一时候,一个CPU只能执行一个线程,所以CPU有几核,就能同时执行几个线程(而在cpython中,这个并不能实用,因为在cpython中,一个进程里面只能让一个线程运行。)
线程才是被CPU真正执行的,而在进程之间的线程是没有任何关系的。
线程的详细介绍:http://www.cnblogs.com/linhaifeng/articles/7430082.html
四 threading模块:开启子线程的模块
Thread:开启子线程
target:后面加上一个函数名
args:传参数,以元组的格式传入
格式:线程=threading.Thread(target=函数名,args=(参数,))
strart:向操作系统发送开启线程的信息(启动线程)
线程的创建方式:
方式1:
# import threading # import os # def work(n): # print('%s is working %s'%(os.getpid(),n)) # # # if __name__=='__main__': # for i in range(10): # t=threading.Thread(target=work,args=(i,)) # t.start() # print('主线程》》》',os.getpid())
方式2:
import threading class Mythread(threading.Thread): def __init__(self,n): super().__init__() self.n=n def run(self): print('%s is working' % self.n) if __name__=='__main__': for i in range(10): t=Mythread(i) t.start() print('主线程》》》')
在同一个进程下,子线程和主线程使用的是统一个pid
join:等待线程执行结束
import threading import os import time import random def work(n): time.sleep(random.random()) print('%s is working %s'%(os.getpid(),n)) if __name__=='__main__': for i in range(10): t=threading.Thread(target=work,args=(i,)) t.start() t.join() print('主线程》》》',os.getpid())
threading的其他使用方法:
getName:获取线程名
setName:设置线程名
carrent_thread:获取当前的线程对象
enumerate:查看当前活跃的线程对象,以列表的格式返回
activeCount:查看当前活跃的线程数目
import threading import os import time import random def work(n): time.sleep(random.random()) print('%s is working %s'%(os.getpid(),n)) print(threading.current_thread().getName()) #获取当前线程名 if __name__=='__main__': for i in range(10): t=threading.Thread(target=work,args=(i,)) t.start() print(threading.enumerate()) #查看当前活跃的对象 print(threading.activeCount()) #查看活跃的数目 print('主线程》》》',os.getpid())
线程执行完毕,被回收的时间长短是不一样的。
守护线程:守护线程会在主线程执行结束后,自动的结束。
主线程从执行的意义上来讲就是一个主进程,主线程的结束会在除守护线程执行结束后就会结束。
daemon=True:创建守护线程
import threading import os import time import random def work(n): print('%s is working %s'%(os.getpid(),n)) time.sleep(random.random()) print('%s is ending %s'%(os.getpid(),n)) def foo(n): print('%s is fooing %s'%(os.getpid(),n)) time.sleep(random.random()) print('%s is ending %s'%(os.getpid(),n)) if __name__=='__main__': t1=threading.Thread(target=work,args=(100,)) t2=threading.Thread(target=foo,args=(200,)) t1.daemon=True t1.start() t2.start() print('主线程》》》',os.getpid())
守护线程等待非守护线程的结束是为了关闭进程。
五 GIL(互斥锁)
1 全局解释互斥锁:保证了数据的安全性。而GIL是cpython解释器的一种特性。
2 互斥锁(mutex):限制了cpython中多个线程的执行数量。与cpython的内存管理有关。
3 cpython中有一个回收机制,绑定的关系引用技术为0时,就会自动的回收调。
一把互斥锁只能保证一类数据的安全性。
import threading import time import random n=20 def work(): global n loak.acquire() time.sleep(random.random()) n-=1 loak.release() if __name__=='__main__': li=[] loak=threading.Lock() for i in range(20): t=threading.Thread(target=work) li.append(t) t.start() for t in li: t.join() print(n)
假如不加上锁,结果会不一样,如下:
import threading import time import random n=200 def work(): global n time.sleep(random.random()) n-=1 if __name__=='__main__': for i in range(200): t=threading.Thread(target=work) t.start() t.join() print(n)
GIL锁只能保证cpython解释器中的代码安全,不能保证自己的代码安全性,如果想要保证自己代码的安全性,就需要给自己的代码加上一把互斥锁
有了GIL锁,就能保证cpython中的进程中的多个线程一只能执行一个。
对于计算密集型来说,在多核CPU下开启多个进程的效率比较高
#使用线程进行计算密集型 # import threading # import time # def work(): # a=1 # for i in range(1000000): # a*=i # if __name__=='__main__': # li=[] # start = time.time() # for i in range(100): # t=threading.Thread(target=work) #13.447768926620483 # li.append(t) # t.start() # for t in li: # t.join() # print(time.time() - start) # 使用进程实现计算密集型 # import multiprocessing # import time # def work(): # a=1 # for i in range(1000000): # a*=i # if __name__=='__main__': # li=[] # start = time.time() # for i in range(100): # t=multiprocessing.Process(target=work) #8.421481609344482 # li.append(t) # t.start() # for t in li: # t.join() # print(time.time() - start)
对于IO密集型来说,多核CPU和单核CPU的效果是一样的,开启多线程的效率会比开启多进程的效率要高,并且还能保证数据的安全性。
#使用线程进行IO密集型 # import threading # import time # def work(): # time.sleep(2) # if __name__=='__main__': # li=[] # start = time.time() # for i in range(10): # t=threading.Thread(target=work) #2.0028274059295654 # li.append(t) # t.start() # for t in li: # t.join() # print(time.time() - start) # 使用进程实现IO密集型 # import multiprocessing # import time # def work(): # time.sleep(2) # if __name__=='__main__': # li=[] # start = time.time() # for i in range(10): # t=multiprocessing.Process(target=work) #2.953113079071045 # li.append(t) # t.start() # for t in li: # t.join() # print(time.time() - start)