1.线程理论
- 线程是CPU的执行单位
- 多线程(即多个控制线程)的概念是,在一个进程中存在多个线程,多个线程共享该进程的地址空间,相当于一个车间内又多条流水线,都共用一个车间的资源。例如,北京地铁与上海地铁是不同的进程,而北京地铁里的13号线是一个线程,北京地铁所有的线路共享北京地铁所有的资源,比如所有的乘客可以被所有线路拉。
2.线程与进程的区别
- 同一个进程内的多个线程共享该进程内的地址资源
- 创建线程的开销要远小于创建进程的开销(创建一个进程,就是创建一个车间,涉及到申请空间,而且在该空间内建至少一条流水线,但创建线程,就只是在一个车间内造一条流水线,无需申请空间,所以创建开销小)
3.开启线程的两种方式
- 方式一:函数
-
1 #-*- coding:utf-8 -*- 2 from threading import Thread 3 import time 4 import os 5 def sayhi(name): 6 time.sleep(2) 7 print("%s say hello"%name) 8 if __name__ == "__main__": 9 t = Thread(target=sayhi,args=('egon',)) 10 t.start() 11 print("主")
- 方式二:类
-
1 class SayHi(Thread): 2 def __init__(self,name): 3 super().__init__() 4 self.name = name 5 def run(self): 6 print("%s say hello" % self.name) 7 print("线程pid:",os.getpid()) 8 if __name__ =="__main__": 9 t1 = SayHi("egon") 10 t2 = SayHi("alex") 11 t1.start() 12 t2.start() 13 print("主进程pid:",os.getpid())
4.多线程与多进程的区别
- 开启速度:线程快于进程
- pid:同一进程下不同线程的pid是相同的,进程之间的pid是不同的
- 内存:进程之间内存地址空间是隔离的,而同一进程内开启的多个线程是共享该进程内存地址空间的
5.Thread对象的其他属性或方法
- Thread实例对象的方法
- isAlive():返回线程是否活动的
- getName():返回线程名
- setName():设置线程名
- threading模块提供的一些方法
- threading.currentThread():返回当前的线程变量
- threading.enumerate():返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前后终止后的线程
- threading.activeCount():返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
-
1 #-*- coding:utf-8 -*- 2 from threading import Thread 3 import time 4 import threading 5 import os 6 7 def sayhi(name): 8 time.sleep(2) 9 print("%s say hello"%name) 10 print(threading.current_thread().getName())#Thread-1 11 if __name__ == "__main__": 12 t = Thread(target=sayhi,args=('egon',)) 13 t.start() 14 print(threading.current_thread().getName())#MainThread 15 print(threading.current_thread())#<_MainThread(MainThread, started 8308)> 16 print(threading.enumerate())#[<_MainThread(MainThread, started 9072)>, <Thread(Thread-1, started 7052)>] 17 print(threading.active_count())#2 18 print(t.is_alive())#True 19 t.join() 20 print('主线程/主进程') 21 print(t.is_alive())#False
5.守护线程
- 无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
- 强调:运行完毕并非终止运行
- 对于主进程来说,运行完毕指的是主进程代码运行完毕
- 对于主线程来说,运行完毕指的是主线程所在进程内所有非守护线程统统运行完毕,主线程才算运行完毕
- 守护线程:在同一进程内,其他非守护线程运行完毕后才算运行完毕,此时守护线程被回收
-
1 #-*- coding:utf-8 -*- 2 from threading import Thread 3 import time 4 5 def walk(): 6 print("start123") 7 time.sleep(1) 8 print("end123") 9 def run(): 10 print("start456") 11 time.sleep(3) 12 print("end456") 13 if __name__ == "__main__": 14 t1 = Thread(target=walk) 15 t2 = Thread(target=run) 16 t1.daemon = True 17 t1.start() 18 t2.start() 19 print("主") 20 21 22 #start123 23 #start456 24 #主 25 #end123 26 #end456
-
1 #-*- coding:utf-8 -*- 2 from threading import Thread 3 import time 4 5 def walk(): 6 print("start123") 7 time.sleep(3) 8 print("end123") 9 def run(): 10 print("start456") 11 time.sleep(1) 12 print("end456") 13 if __name__ == "__main__": 14 t1 = Thread(target=walk) 15 t2 = Thread(target=run) 16 t1.daemon = True 17 t1.start() 18 t2.start() 19 print("主") 20 #start123 21 #start456 22 #主 23 #end456
6.GIL全局解释器锁
- 本质上也是互斥锁
- 保护不同的数据应该加不同的锁,GIL是解释器级别的(当然保护的就是解释器级别的数据,比如垃圾回收的数据);lock是保护用户自己开发的应用程序的数据,很明显GIL不负责这件事,只能用户自己加锁定义
- 有了GIL的存在,同一时刻同一进程中只能有一个线程被执行
- GIL与多线程
- ·对于计算来说,CPU越多越好,但是对于I/O来说,再多的CPU也没用
- 对于单核计算机来说,常采用开启一个进程,多个线程的方案
- 对于多核计算机来说,如果任务是计算密集型,多核意味着并行计算,多进程方案更优;如果任务是I/O密集型,则多线程方案更优
- 多线程用于IO密集型,如socket,爬虫,web
- 多进程用于计算密集型,如金融分析
-
1 #-*- coding:utf-8 -*- 2 from multiprocessing import Process 3 from threading import Thread 4 import os 5 import time 6 def work(): 7 time.sleep(2) 8 print("===>") 9 if __name__ == "__main__": 10 l_p = [] 11 print(os.cpu_count()) 12 start = time.time() 13 for i in range(400): 14 # p = Process(target=work)#耗时时间长 15 p = Thread(target=work)#耗时时间短 16 l_p.append(p) 17 p.start() 18 for p in l_p: 19 p.join() 20 stop = time.time() 21 print("run time is %s"%(stop-start))
1 #-*- coding:utf-8 -*- 2 from multiprocessing import Process 3 from threading import Thread 4 import os 5 import time 6 def work(): 7 res = 0 8 for i in range(100000000): 9 res*=1 10 if __name__ == "__main__": 11 l_p = [] 12 print(os.cpu_count()) 13 start = time.time() 14 for i in range(4): 15 # p = Process(target=work)#耗时时间短 16 p = Thread(target=work)#耗时时间长 17 l_p.append(p) 18 p.start() 19 for p in l_p: 20 p.join() 21 stop = time.time() 22 print("run time is %s"%(stop-start))
7.死锁现象与递归锁RLOCK
- 死锁现象
- 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁
-
1 # -*- coding:utf-8 -*- 2 from threading import Thread, Lock 3 import time 4 5 mutexA = Lock() 6 mutexB = Lock() 7 8 9 class MyThread(Thread): 10 def run(self): 11 self.func1() 12 self.func2() 13 14 def func1(self): 15 mutexA.acquire() 16 print('