1、线程的理论python
一、什么是线程多线程
1) 在传统操做系统中,每一个进程有一个地址空间,并且默认就有一个控制线程ide
2)线程顾名思义,就是一条流水线工做的过程(流水线的工作须要电源,电源就至关于cpu),而一条流水线必须属于一个车间,ui
一个车间的工做过程就是一个进程,车间负责把资源整合到一块儿,是一个资源单位,而一个车间至少有一条流水线。 spa
3)因此:进程只是用来把资源集中到一块儿(进程只是一个资源单位,或者时候资源集合),而线程才是cpu上的执行单位操作系统
4) 多线程(即多个控制线程)的概念是,在一个进程中存在多个线程,多个线程共享该进程的地址空间,至关与一个车间内有线程
多天流水线,都公用一个车间的资源。例如,北京地铁与上海地铁是不一样的进程,而北京地铁里的13号线是一个线程,北京地铁全部设计
的线路共享北京地铁全部的资源,好比全部的乘客能够被全部线路拉。3d
二、线程与进程的区别code
1)同一个进程内的多个线程共享该进程内的地址资源
2)建立线程的开销要远小于建立进程的开销(建立一个进程,就是建立一个车间,设计到申请空间,并且在该空间
内至少建一条流水线,但建立线程,就只是在一个车间内造一条流水线,无需申请空间,因此建立开销小)
2、开启线程的两种方式
一、threading模块介绍
multiprocess模块的彻底模仿了threading模块的接口,两者在使用层面,有很大的类似性,由于不在详细的介绍
二、开启线程的两种方式
1 # 开启线程的两种方式 2 3 # 方式一 4 # from threading import Thread 5 # import time 6 # 7 # def sayhi(name): 8 # time.sleep(2) 9 # print('%s say hello'%name) 10 # 11 # if __name__ == '__main__': 12 # t = Thread(target=sayhi,args=('egon',)) 13 # t.start() 14 # print('主线程') 15 16 # 方式二 17 import time 18 from threading import Thread 19 20 class Sayhi(Thread): 21 def __init__(self,name): 22 super().__init__() 23 self.name = name 24 25 def run(self): 26 time.sleep(2) 27 print('%s say hello'%self.name) 28 29 if __name__ == '__main__': 30 t = Sayhi('egon') 31 t.start() 32 print('主线程')
3、多线程和多进程的区别
一、谁的开启速度快
from threading import Thread def work(): print('hello') if __name__ == '__main__': t = Thread(target=work) t.start() print('主线程/主进程')
执行结果以下,几乎t.start()的同时就将线程开启了,而后打印出了hello,证实 线程的建立开销极小
hello 主线程/主进程
from multiprocessing import Process def work(): print('hello') if __name__ == '__main__': #在主进程下开启子进程 p = Process(target=work) p.start() print('主进程/主线程') # 执行结果以下,p.start()将开启进程的信号发给操做系统,操做系统要申请内存 # 空间,拷贝父进程地址空间到子进程,开销远大于线程 # 主进程/主线程 # hello
二、瞅一瞅 pid
1 # 在主进程下开启多个线程,每一个线程都跟主进程的pid同样 2 # from threading import Thread 3 # import os 4 # 5 # def work(): 6 # print('hello',os.getpid()) 7 # 8 # if __name__ == '__main__': 9 # t1 = Thread(target=work) 10 # t2 = Thread(target=work) 11 # t1.start() 12 # t2.start() 13 # print('主线程/主进程pid',os.getpid()) 14 # 15 # 结果: 16 # hello 9856 17 # hello 9856 18 # 主线程/主进程pid 9856 19 20 # 开多个进程,每一个进程都有不一样的pid 21 # from multiprocessing import Process 22 # import os 23 # 24 # def work(): 25 # print('hello',os.getpid()) 26 # 27 # if __name__ == '__main__': 28 # p1 = Process(target=work) 29 # p2 = Process(target=work) 30 # p1.start() 31 # p2.start() 32 # print('主进程/主线程',os.getpid()) 33 # 34 # 结果: 35 # 主进程/主线程 38384 36 # hello 37008 37 # hello 36496
三、同一进程内的线程共享该进程的数据?
1 # 进程之间地址空间是隔离的 2 # from multiprocessing import Process 3 # import os 4 # 5 # def work(): 6 # global n 7 # n = 0 8 # 9 # if __name__ == '__main__': 10 # n = 100 11 # p = Process(target=work) 12 # p.start() 13 # p.join() 14 # print('主',n) 15 # 16 # 结果分析:毫无疑问子进程p已将本身的全局n改为了0.可是改的仅仅是本身的 17 # 父进程的n仍然为100 18 19 20 # 同一进程内开启的多个线程是共享该进程地址空间的 21 # from threading import Thread 22 # import os 23 # 24 # def work(): 25 # global n 26 # n = 0 27 # if __name__ == '__main__': 28 # n = 100 29 # t = Thread(target=work) 30 # t.start() 31 # t.join() 32 # print('主',n) 33 # 结果:查看结果为0,由于统一进程内的线程之间共享进程内的数据 34 # 主 0
4、Thread 对象的其余属性或方法
介绍:
# isAlive(): 返回线程是否活动的。 # getName(): 返回线程名。 # setName(): 设置线程名 threading 模块提供的一些方法: # threading.currentThread(): 返回当前线程变量 # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程 # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果
验证:
1 # from threading import Thread 2 # import threading 3 # from multiprocessing import Process 4 # import os,time 5 # 6 # def work(): 7 # time.sleep(3) 8 # print(threading.current_thread().getName()) 9 # if __name__ == '__main__': 10 # # 在主进程下开启线程 11 # t = Thread(target=work) 12 # t.start() 13 # print(threading.current_thread().getName()) 14 # print(threading.current_thread()) 15 # print(threading.enumerate()) 16 # print(threading.activeCount) 17 # print('主进程\主线程') 18 # 19 # 结果: 20 # MainThread 21 # <_MainThread(MainThread, started 39404)> 22 # [<_MainThread(MainThread, started 39404)>, <Thread(Thread-1, started 39468)>] 23 # <function active_count at 0x000001CBF2F45BF8> 24 # 主进程\主线程 25 # Thread-1
1 # 主线程等待子进程结束 2 # from threading import Thread 3 # import time 4 # def sayhi(name): 5 # time.sleep(2) 6 # print('%s say hello'%name) 7 # if __name__ == '__main__': 8 # t = Thread(target=sayhi,args=('egon',)) 9 # t.start() 10 # t.join() 11 # print('主进程') 12 # print(t.is_alive()) 13 # 14 # 执行结果: 15 # egon say hello 16 # 主进程 17 # False
5、守护线程
不管是进程仍是线程,都遵循:守护xxx会主动等待主xxx运行完毕后被销毁
须要强调的是:运行完毕并不是终止运行
一、对主进程来讲,运行完毕指的是主进程代码运行完毕 二、对主线程来讲,运行完毕指的是主线程所在的进程内全部非守护线程通通运行完毕,主线程才算运行完毕 详细解释: 一、主进程在其代码结束后就已经运算完毕了(守护进程在此时就被回收),而后主进程 会一直等飞受贿的子进程都运行完毕后回收子进程的资源(不然会产生僵尸进程),才结束 二、主线程在其余费守护线程运行完毕后才算运行完毕(守护线程在刺死就被回收)。由于主线程的结束意味着进程的结束,基础讷航总体的资源都将被回收,而基础讷航必须保证非守护线程都运行完毕后才能结束。
验证:
# 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
6、死锁现象与递归锁
一、所谓死锁:是指两个或两个以上的进程或线程执行过程当中,由于争夺资源而形成的一种互相等待的现象,若无外力
做用,它们都将没法推动下去。此时称系统处于死锁或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,以下就是死锁
1 # from threading import Thread,Lock 2 # import time 3 # 4 # muteA = Lock() 5 # muteB = Lock() 6 # 7 # class MyThread(Thread): 8 # def run(self): 9 # self.fun1() 10 # self.fun2() 11 # 12 # def fun1(self): 13 # muteA.acquire() 14 # print('%s拿到A锁'%self.name) 15 # 16 # muteB.acquire() 17 # print('%s 拿到B锁'%self.name) 18 # muteB.release() 19 # muteA.release() 20 # 21 # def fun2(self): 22 # muteB.acquire() 23 # print("%s 拿到B锁"%self.name) 24 # time.sleep(2) 25 # 26 # muteA.acquire() 27 # print("%s 拿到A锁"%self.name) 28 # muteA.release() 29 # 30 # muteB.release() 31 # 32 # if __name__ == '__main__': 33 # for i in range(10): 34 # t = MyThread() 35 # t.start() 36 # 37 # 结果: 38 # Thread-1拿到A锁 39 # Thread-1 拿到B锁 40 # Thread-1 拿到B锁 41 # Thread-2拿到A锁
二、递归锁
解决办法:递归锁,在python中为了支持在同一线程中屡次请求同一资源,python提供了可重入锁RLock个RLock内部维护着
一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源能够被屡次require。直到一个线程全部的acquire都
被release,其余的线程才能得到资源。上面的例子若是使用RLock代替Lock,则不会发生死锁,两者的区别是:递归锁能够连续
acquire屡次,而互斥锁只能acquire一次
1 from threading import Thread,RLock 2 import time 3 4 mutexA=mutexB=RLock() #一个线程拿到锁,counter加1,该线程内又碰到加锁的状况,则counter继续加1,这期间全部其余线程都只能等待,等待该线程释放全部锁,即counter递减到0为止 5 6 class MyThread(Thread): 7 def run(self): 8 self.func1() 9 self.func2() 10 def func1(self): 11 mutexA.acquire() 12 print('\033[41m%s 拿到A锁\033[0m' %self.name) 13 14 mutexB.acquire() 15 print('\033[42m%s 拿到B锁\033[0m' %self.name) 16 mutexB.release() 17 18 mutexA.release() 19 20 def func2(self): 21 mutexB.acquire() 22 print('\033[43m%s 拿到B锁\033[0m' %self.name) 23 time.sleep(2) 24 25 mutexA.acquire() 26 print('\033[44m%s 拿到A锁\033[0m' %self.name) 27 mutexA.release() 28 29 mutexB.release() 30 31 if __name__ == '__main__': 32 for i in range(10): 33 t=MyThread() 34 t.start()