Python3 从零单排25_多线程
进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位。
公司=Cpu,部门=进程,开发人员=线程
部门隔离数据,财务部的财务数据不会给开发部门看,开发部门不是实际干活的单位,部门里的开发人员才干活,一个部门至少要有一个人才能干活,这就是主线程。
多线程(即多个控制线程)
在一个进程中存在多个线程,多个线程共享该进程的地址空间,也就是说,同一个进程的线程是共享进程数据的
1.同一个进程内的多个线程共享该进程内的地址资源
2.创建线程的开销要远小于创建进程的开销(创建进程,需要申请内存,而且建至少一个线程来干活;建线程,只是在进程内造一个线程,无需申请内存)
Thread实例对象的方法
# isAlive(): 返回线程是否活动的。
# getName(): 返回线程名。
# setName(): 设置线程名。
threading模块提供的一些方法:
# threading.currentThread(): 返回当前的线程变量。
# threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
# threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
1.实现多线程的两种方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 跟多进程一样,开启线程也是两种方式 2 ''' 3 from threading import Thread, currentThread, enumerate, activeCount 4 import time 5 import os 6 7 # 方式一: 8 def task(name): 9 print("%s is running...,pid: %s;" % (name, os.getpid())) # 拿到进程pid 10 time.sleep(1) 11 print("%s is done..." % name) 12 print("子线程%s 当前的线程名:%s" % (name, currentThread())) 13 14 15 # 方式二 16 class CreateThread(Thread): 17 def __init__(self, name): 18 super().__init__() 19 self.name = name # 如果没指定名字,会有自己的名称,类名+编号 20 21 def run(self): 22 print("%s is running...,pid: %s;" % (self.name, os.getpid())) 23 time.sleep(3) 24 print("%s is done..." % self.name) 25 26 27 if __name__ == "__main__": 28 # 方式一: 29 p1 = Thread(target=task, args=("方式一子线程",)) 30 p1.start() # 告诉操作系统帮忙开启新的子线程,操作系统直接创建一个子线程,数据和进程共享 31 print(p1.name) # 线程名称,Thread-编号 也可以自己指定 32 # p4 = Thread(target=task, name="我是方式一的子线程4444", args=("方式一子线程4",)) # name 指定线程名称 33 # print(p4.name) 34 35 # 方式二: 36 p2 = CreateThread("方式二子线程1") 37 p3 = CreateThread("方式二子线程2") 38 p2.start() 39 p3.start() 40 41 print(p1.is_alive()) # true 42 print(p3.is_alive()) # true 43 print("p1 join之前的线程数 %s" % activeCount()) 44 print("p1 join之前的线程对象列表 %s" % enumerate()) 45 p1.join() # 等待子线程p1运行结算才往下运行 46 print("p1 join之后的线程数 %s" % activeCount()) 47 print("p1 join之后的线程对象列表 %s" % enumerate()) 48 print(p1.is_alive()) # 判断线程存活状态,因为上面join了,所以p1已经是结束了的,只是保留了状态,所以false 49 print("主线程结束! pid: %s" % os.getpid()) # 拿到进程pid 50 print("主线程的线程名:%s" % currentThread())
2.进程与线程的区别
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 1、开进程的开销远大于开线程 2 import time 3 from threading import Thread 4 from multiprocessing import Process 5 6 def piao(name): 7 print('%s piaoing' %name) 8 time.sleep(2) 9 print('%s piao end' %name) 10 11 if __name__ == '__main__': 12 # p1=Process(target=piao,args=('egon',)) 13 # p1.start() 14 15 t1=Thread(target=piao,args=('egon',)) 16 t1.start() 17 print('主线程') 18 19 20 21 # 2、同一进程内的多个线程共享该进程的地址空间 22 from threading import Thread 23 from multiprocessing import Process 24 25 n=100 26 def task(): 27 global n 28 n=0 29 30 if __name__ == '__main__': 31 # p1=Process(target=task,) 32 # p1.start() 33 # p1.join() 34 35 t1=Thread(target=task,) 36 t1.start() 37 t1.join() 38 39 print('主线程',n) 40 41 42 # 3、瞅一眼pid 43 from threading import Thread 44 from multiprocessing import Process,current_process 45 import os 46 47 def task(): 48 # print(current_process().pid) 49 print('子进程PID:%s 父进程的PID:%s' %(os.getpid(),os.getppid())) 50 51 if __name__ == '__main__': 52 p1=Process(target=task,) 53 p1.start() 54 55 # print('主线程',current_process().pid) 56 print('主线程',os.getpid()) 57 58 59 from threading import Thread 60 import os 61 62 def task(): 63 print('子线程:%s' %(os.getpid())) 64 65 if __name__ == '__main__': 66 t1=Thread(target=task,) 67 t1.start() 68 69 print('主线程',os.getpid())
3.守护线程
无论是进程还是线程,都遵循:守护xxx会等待主xxx运行完毕后被销毁
需要强调的是:运行完毕并非终止运行
强调:对进程和线程来说,运行完毕都不是指主代码运行完毕,而是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕,从而主进程才算完毕
1、主进程达到上述条件后结束(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束
2、主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。
因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 from threading import Thread 2 from multiprocessing import Process 3 import time 4 5 6 def syahi(name): 7 time.sleep(1) 8 print('%s say hi' % name) 9 10 11 def sayhello(name): 12 time.sleep(2) 13 print('%s say hello' % name) 14 15 16 if __name__ == '__main__': 17 # t1=Thread(target=syahi,args=('Thread',)) 18 t2=Thread(target=sayhello,args=('Thread',)) 19 # t1.setDaemon(True) # 必须在t.start()之前设置 20 # t1.start() 21 t2.start() 22 23 p1=Process(target=syahi, args=('Process',)) 24 # p2=Process(target=sayhello, args=('Process',)) 25 p1.daemon = True # 必须在t.start()之前设置 26 p1.start() 27 # p2.start() 28 29 # print('主线程') 30 # print(t1.is_alive()) 31 print(t2.is_alive()) 32 33 # print('主进程') 34 print(p1.is_alive()) 35 # print(p2.is_alive())
4.互斥锁
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 # 下面的代码如果不加互斥锁的话,会n结果是99,因为每个线程都到了time.sleep(0.1)之前拿到的n都是100 2 from threading import Thread,Lock 3 import time 4 n = 100 5 6 7 def task(): 8 global n 9 mutex.acquire() 10 temp = n 11 time.sleep(0.1) 12 n = temp - 1 13 mutex.release() 14 15 16 if __name__ == '__main__': 17 mutex = Lock() 18 t_l = [] 19 for i in range(100): 20 t = Thread(target=task) 21 t_l.append(t) 22 t.start() 23 24 for t in t_l: 25 t.join() 26 27 print('主', n)