Python线程详解
# 进程的三状态:就绪 运行 阻塞 # multiprocessing模块 # Process-开启进程 # Lock - 互斥锁 # 为什么要在进程中加锁 # 因为进程操作文件也会发生数据不安全 # Queue -队列 IPC机制(Pipe,redis,memcache,rabbitmq,kafka) # 生产者消费者模型 # Manager - 提供数据共享机制
------------------Process-开启进程----------
可查看链接https://www.cnblogs.com/Marcki/p/10111927.html ---------线程学习-------- 1、启动线程start 线程的异步2、开启多个子线程
3、join方法 4、测试 进程和线程的开启效率差 数据隔离还是共享 5、守护线程1)启动线程start
(1从线程导入线程 2实例化Thread,传递线程函数。3对象.start())
```
import os from threading import Thread #multiprocessing完全是仿照threading类写的 def func(): print(os.getpid()) #启动线程 start Thread(target=func).start() print('---->',os.getpid()) -------------结果: 5804 ----> 5804```
#由上可知,1、主线程和子线程在同一个进程里,pid一致。2、在Windows里面不需要写if name=='main'了。3、先打印子线程里的内容,再打印主线程里内容,与进程相反,说明线程开启快,快到主线程还没执行下一句代码这个子线程就创建并执行了。2)线程是异步的,并发的
```
import os,time
from threading import Thread def func(): print('start son thread') time.sleep(1) print('end son thread') Thread(target=func).start() print('start',os.getpid()) time.sleep(0.5) print('end',os.getpid()) --------------结果: start son thread start 5280 end 5280 end son thread```
3)开启多个子线程
import os,time
from threading import Thread def func(): print('start son thread') time.sleep(1) print('end son thread',os.getpid()) for i in range(3): Thread(target=func).start() ----------------结果: start son thread start son thread start son thread end son thread 6772 end son thread 6772 end son thread 67724)往线程函数里传参,通过args,和进程一样。线程的调度仍然是操作系统决定的,谁的时间片到了就运行谁的
```
import os,time
from threading import Thread def func(arg): print('start son thread') time.sleep(1) print('end son thread',os.getpid(),arg) for i in range(3): Thread(target=func,args=(i,)).start() ------------结果: start son thread start son thread start son thread end son thread 7112 0 end son thread 7112 1 end son thread 7112 2```
5)1、主线程等待所有子线程结束之后才结束。2、主线程如果结束了,主进程也就结束了。
import os,time
from threading import Thread def func(arg): print('start son thread') time.sleep(1) print('end son thread',os.getpid(),arg) for i in range(3): Thread(target=func,args=(i,)).start() print("main") --------------结果: start son thread start son thread start son thread main #主线程代码执行完了,等待子线程结束 end son thread 5416 0 end son thread 5416 1 end son thread 5416 2 Process finished with exit code 0 #所有子线程结束,主线程才结束,然后主进程结束 5)[1]join方法 阻塞 直到子线程执行结束 #用join,那么线程对象和start就不要放在一起了,单执行对象.start()来开启 import os,time from threading import Thread def func(arg): print('start son thread') time.sleep(1) print('end son thread',os.getpid(),arg) t=Thread(target=func,args=(0,)) t.start() t.join() print("子线程执行结束!") ----------------------结果: start son thread end son thread 6756 0 子线程执行结束! [2]创建多个子线程,t.join()在循环外,这时t代表for循环最后一个值3,所有只是阻塞最后一个创建的t对象。所以每次打印“子线程执行结束!”都是在“end son thread 4864 3”之后。但是这时执行慢的子线程就没被阻塞就开始执行主线程中代码了。 import os,time from threading import Thread def func(arg): print('start son thread') time.sleep(1) print('end son thread',os.getpid(),arg) for i in range(4): t=Thread(target=func,args=(i,)) t.start() t.join() print("子线程执行结束!") --------------结果: start son thread start son thread start son thread start son thread end son thread 4864 0 end son thread 4864 2 end son thread 4864 3 子线程执行结束! end son thread 4864 1 [3]保证所有子线程都结束之后再继续执行主线程中的代码 #注:将所有线程对象都追加到列表,循环列表,将每个对象.join阻塞。所有线程对象执行结束然后才执行主线程中程序 import os,time from threading import Thread def func(arg): print('start son thread') time.sleep(1) print('end son thread',os.getpid(),arg) li=[] for i in range(4): t=Thread(target=func,args=(i,)) t.start() li.append(t) for t in li:t.join() print("子线程执行结束!") --------------结果: start son thread start son thread start son thread start son thread end son thread 6480 0 end son thread 6480 1 end son thread 6480 2 end son thread 6480 3 子线程执行结束!6)[1]使用面向对象的方式启动线程
#注意:用自己类创建的对象记得start开启线程
#1、继承Thread类2、定义run方法,run方法是线程执行函数 import os,time from threading import Thread class MyThread(Thread): def run(self): print('start son thread') time.sleep(1) print('end son thread',os.getpid()) for i in range(3): MyThread().start() ------------------结果: start son thread start son thread start son thread end son thread 6116 end son thread 6116 end son thread 6116 [2]面向对象启动线程并往线程函数里传参 #1、创建init方法,传参进入类中 2、执行父类的init方法,不执行报错入下 import os,time from threading import Thread class MyThread(Thread): def init(self,name): super().init() #执行父类init方法在实例变量后面报错了,不知道怎么回事,有时间测试一下 self.name=namedef run(self): time.sleep(1) print('end son thread',os.getpid(),self.name)
for i in range(3):
MyThread("mcw").start() ------------------结果: end son thread 2060 mcw end son thread 2060 mcw end son thread 2060 mcw不执行父类init报错:
assert self._initialized, "Thread.init() not called" AssertionError: Thread.init() not called[3]start执行的原理
class Foo: def start(self): self.run() def run(self): print('run in Foo') class Son(Foo): def run(self): print('run in son') Son().start() ---------------结果: run in son [4]自定义类中把init方法去掉后,实例化的时候还带有传参报错 t=MyThread("mcw") File "C:\python3\lib\", line 780, in init assert group is None, "group argument must be None for now" AssertionError: group argument must be None for now7)线程里面的其它方法 查看线程id
[1]用类来启动线程,类里面和外面查看线程的id。对象.ident self.ident。注意:在init里定义与父类相同的实例变量名,会被覆盖掉的。
import os,time from threading import Thread class MyThread(Thread): def run(self): time.sleep(1) print(' thread id',self.ident) for i in range(3): t=MyThread() t.start() print("线程id",t.ident) [2]current_thread()在哪个线程里代表哪个线程。用函数来启动线程,查看线程id 。 #当前线程current_thread()对象,记得加括号(),函数中调用对象 import time from threading import current_thread,Thread def func(i): print('start son thread',i,current_thread(),type(current_thread())) print("线程函数中调用执行这个函数的线程对象的线程id:%s"%current_thread().ident) time.sleep(1) print('end son thread',) t=Thread(target=func,args=(1,)) t.start() print("main",t,t.ident) --------------结果: start son thread 1 <Thread(Thread-1, started 4444)> <class 'threading.Thread'> 线程函数中调用执行这个函数的线程对象的线程id:4444 main <Thread(Thread-1, started 4444)> 4444 end son threadfrom threading import current_thread,Thread
def func(): print('end son thread',current_thread().ident) t=Thread(target=func).start() print("main thread",current_thread().ident) ------------结果: end son thread 1936 #current_thread()在哪个线程里代表哪个线程 main thread 4928 [3]enumerate active_count from threading import current_thread,Thread,active_count,enumerate def func(): print('end son thread',current_thread().ident) t=Thread(target=func).start() print(enumerate(),enumerate) #主线程和子线程两个。enumerate()表示当前运行的线程, print(active_count(),active_count) #active_count()代表当前运行的线程的个数,相当于len(enumerate()) -------------------结果: end son thread 4324 2 [<_MainThread(MainThread, started 2460)>, <Thread(Thread-1, started 4324)>] <function enumerate at 0x006E1270> 2 <function active_count at 0x006E11E0> [4]terminate 能结束进程 。 在线程中不能从主线程结束一个子线程8)进程和线程的开启效率差。进程一般开cpu个数的1到2倍,开多了浪费系统资源
import time
from threading import Thread from multiprocessing import Process def func(a,b): c=a+b if name=="main": p_start=time.time() p_li=[] for i in range(100): p=Process(target=func,args=(i,i2)) p.start() p_li.append(p) for p in p_li:p.join() print("进程:",time.time()-p_start) t_start=time.time() t_li = [] for i in range(100): t=Thread(target=func,args=(i,i2)) t.start() t_li.append(t) for t in t_li:t.join() print("线程:",time.time() - t_start) ----------------结果: 进程: 6.1583521366119385 线程: 0.01500082015991211 #几百倍速度差距9)线程共享进程中的数据。子线程在进程中共享数据,
from threading import Thread
n=100 def func(): global n #最好不要轻易修改全局变量 n-=1 t_li=[] for i in range(100): t=Thread(target=func) t_li.append(t) t.start() for t in t_li:t.join() print(n) -----------结果: 010)守护线程
[1]没有守护线程的时候
import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") t=Thread(target=son1) t.start() ---------结果: 一直打印“in son1” [2]有守护线程的时候,线程开启后,后面没有运行一段时间的代码 import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") t=Thread(target=son1) t.daemon=True t.start() ---------------结果: 什么都没有打印 #这说明后面没代码了,主线程结束守护线程就结束了 [3]有守护线程的时候,线程开启后,后面有运行一段时间的代码time.sleep(3) import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") t=Thread(target=son1) t.daemon=True t.start() time.sleep(3) ----------结果: 打印5次"in son1"后结束 #这说明后面还有代码,主线程执行完代码结束后守护线程就结束了 [4] import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") t=Thread(target=son1) t.daemon=True t.start() time.sleep(3.1) ---------结果: 打印6次"in son1"后结束 #和上面打印5次对比,5次是因为主线程执行的3秒内,守护线程一直在运行,守护线程0.5秒打印一次,3秒到了主线程结束守护线程3秒也就结束。守护线程每0.5秒后打印一次,3秒后因为主线程停止守护线程也停止所以没有打印第6次 #这里将主线程运行时间+0.1,也就是主线线程运行时间大于3秒,那么守护线程也运行3秒以上,刚过3秒就运行到打印“in son1”,所以打印6次 [5] import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") def son2(): for i in range(5): time.sleep(1) print("in son2") t=Thread(target=son1) t.daemon=True t.start() Thread(target=son2).start() time.sleep(3) -----------结果: in son1 in son2 in son1 ....... "in son2"打印5次,"in son1"打印9次 #这说明守护线程一直等到所有的线程都结束之后才结束的 #这说明守护线程除了守护主线程的代码之外也会守护子线程 #主线程代码执行完毕,主线程没有关闭。主线程等待所有子线程结束之后才结束。守护线程守护主线程,所以守护线程也要等所有子线程结束之后才结束。如果子线程都结束了,主线程还有程序在运行,那么守护线程还是在守护主线程。 [6] import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") t=Thread(target=son1) t.daemon=True t.start() time.sleep(3) print('1') print('1') print('1') ----------结果: 六次 “in son1” 1 1 1 #原因:为什么主线程有代码在运行的时候,守护线程没有做打印操作了呢,因为这里守护线程是0.5s打印一次,主线程打印3次“1”所花费的时间不足0.5s不够守护线程到达下一次打印的时间。为什么打印6次呢,因为主线程sleep3秒正好达到守护线程打印第6次的时间,在主线程第3秒到打印出第一个“1”这个时间之间,守护线程才打印出第6个“in son1”。time.sleep(3)后面没有代码,守护线程在第3秒是没有执行到打印就随着主线程停止而停止了的。 [7] import time from threading import Thread def son1(): while True: time.sleep(0.5) print("in son1") t=Thread(target=son1) t.daemon=True t.start() time.sleep(3) print('1') time.sleep(1) print('1') ---------------结果: 六个“in son1” 然后: 1 in son1 in son1 111)在多进程里启动多线程
import os
from multiprocessing import Process from threading import Threaddef tfunc():
print('tfunc线程',os.getpid()) def pfunc(): print('pfunc进程-->',os.getpid()) Thread(target=tfunc).start()if name == 'main':
Process(target=pfunc).start() ------------------结果: pfunc进程--> 9716 tfunc线程 9716方法总结:
开启进程执行进程函数 进程函数里开启线程执行线程函数 线程函数里执行线程运行的程序