线程与进程
进程
进程就是一个程序在一个数据集上的一次动态执行过程。进程一般由程序、数据集、进程控制块三部分组成。我们编写的程序用来描述进程要完成哪些功能以及如何完成;数据集则是程序在执行过程中所需要使用的资源;进程控制块用来记录进程的外部特征,描述进程的执行变化过程,系统可以利用它来控制和管理进程,它是系统感知进程存在的唯一标志。
线程
线程的出现是为了降低上下文切换的消耗,提高系统的并发性,并突破一个进程只能干一样事的缺陷,使到进程内并发成为可能。
进程和线程的关系:
(1)一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。
(2)资源分配给进程,同一进程的所有线程共享该进程的所有资源。
(3)CPU分给线程,即真正在CPU上运行的是线程。
串行 并行 并发
多年前单核cpu同时执行两个程序就是并发执行.
同步异步
在计算机领域,同步就是指一个进程在执行某个请求的时候,若该请求需要一段时间才能返回信息,那么这个进程将会一直等待下去,直到收到返回信息才继续执行下去;异步是指进程不需要一直等下去,而是继续执行下面的操作,不管其他进程的状态。当有消息返回时系统会通知进程进行处理,这样可以提高执行的效率。举个例子,打电话时就是同步通信,发短息时就是异步通信。
threading模块
普通执行脚本时 是串行
import time def coun(n): print('running on number: %s' % n) time.sleep(n) start = time.time() coun(3) coun(2) print('用时:%s秒' % (time.time() - start)) #串行,从上到下依次执行,执行完共耗时5秒 running on number: 3 running on number: 2 用时:5.0006632804870605秒
多线程
import time import threading #开多线程的模块 def coun(n): print('running on number: %s' % n) time.sleep(n) start = time.time() t1 = threading.Thread(target=coun,args=(3,)) #此处 开第一个子线程实传入参数3 此处还是主线程执行的 t2 = threading.Thread(target=coun,args=(2,)) #此处 开第二个子线程实传入参数2 此处也是主线程执行的 t1.start() #到此处执行子线程1 t2.start() #到此处执行子线程2 print('用时:%s秒' % (time.time() - start)) #整个代码除了 t1.start() t2.start()是子线程执行的其他都是主线程执行的 running on number: 3 running on number: 2 用时:0.000995635986328125秒 #主线程并没有 执行函数,因此主线程耗时0.0秒 函数由子线程执行
当启动子线程时,子线程和主线程 并发着一起执行
join()
在子线程运行完之前,这个子线程的主线程一直被阻塞,等子线程运行完在继续运行主线程
import time import threading #开多线程的模块 def coun(n): print('running on number: %s' % n) time.sleep(n) print('执行结束') start = time.time() t1 = threading.Thread(target=coun,args=(3,)) #此处 开第一个子线程实传入参数3 t2 = threading.Thread(target=coun,args=(2,)) #此处 开第二个子线程实传入参数3 t1.start() #到此处执行子线程1 t2.start() #到此处执行子线程2 t1.join() #在t1运行完之前,主线程一直被阻塞 t2.join() #在t2运行完之前,主线程一直被阻塞 print('用时:%s秒' % (time.time() - start)) #只有在t1和t2共同 运行完之后才继续运行主线程 running on number: 3 running on number: 2 执行结束 执行结束 用时:3.002174139022827秒
循环多个子线程并在最慢的运行完成后结束主线程的方法:
import time import threading # 开多线程的模块 def c(a): print('%s开始' %(a)) if a == 5: #如果是5等待1秒 time.sleep(1) print('%s运行完成'% (a)) elif a == 1: #如果是1等待5秒 time.sleep(5) print('%s运行完成' % (a)) else: time.sleep(a) print('%s运行完成' % (a)) threadd_list = [] d = time.time() for i in range(1, 6): #循环开新的子进程 t = threading.Thread(target=c,args=(i,)) threadd_list.append(t) #将每个子线程的t加入到列表 t.start() #启动子进程 for i in threadd_list: i.join() #循环等待所有子进程结束在继续运行 print('共用时%s' % (time.time() - d)) #共耗时 5秒,最慢的线程5秒
1开始
2开始
3开始
4开始
5开始
5运行完成
2运行完成
3运行完成
4运行完成
1运行完成
共用时5.00162410736084
错误方法1:
import time import threading # 开多线程的模块 def c(a): print('%s开始' %(a)) if a == 5: #如果是5等待1秒 time.sleep(1) print('%s运行完成'% (a)) elif a == 1: #如果是1等待5秒 time.sleep(5) print('%s运行完成' % (a)) else: time.sleep(a) print('%s运行完成' % (a)) threadd_list = [] d = time.time() for i in range(1, 6): #循环开新的子进程 t = threading.Thread(target=c,args=(i,)) threadd_list.append(t) t.start() #启动子进程 t.join() #这样在循环里join()会在每次循环时都要等待,结果和串行一样 print('共用时%s' % (time.time() - d)) #共耗时 1开始 1运行完成 2开始 2运行完成 3开始 3运行完成 4开始 4运行完成 5开始 5运行完成 共用时15.006914854049683
错误方法2:
import time import threading # 开多线程的模块 def c(a): print('%s开始' %(a)) if a == 5: #如果是5等待1秒 time.sleep(1) print('%s运行完成'% (a)) elif a == 1: #如果是1等待5秒 time.sleep(5) print('%s运行完成' % (a)) else: time.sleep(a) print('%s运行完成' % (a)) threadd_list = [] d = time.time() for i in range(1, 6): #循环开新的子进程 t = threading.Thread(target=c,args=(i,)) threadd_list.append(t) t.start() #启动子进程 t.join() #放在循环外,当循环结束时,会执行最后循环的t ,最后的是5对应是1秒 print('共用时%s' % (time.time() - d)) #共耗时 1开始 2开始 3开始 4开始 5开始 5运行完成 共用时1.0022823810577393 2运行完成 3运行完成 4运行完成 1运行完成
setDaemon(True)
将线程声明为守护线程,必须在start()方法之前设置,作用是当主线程运行完成后,不管子线程是否运行完,都随主线程一起退出,与join的作用相反,join是主线程等待子线程结束在继续往下执行
import time import threading # 开多线程的模块 def c(a): print('%s开始' %(a)) if a == 5: #如果是5等待1秒 time.sleep(1) print('%s运行完成'% (a)) elif a == 1: #如果是1等待5秒 time.sleep(5) print('%s运行完成' % (a)) else: time.sleep(a) print('%s运行完成' % (a)) threadd_list = [] d = time.time() for i in range(1, 6): #循环开新的子进程 t = threading.Thread(target=c,args=(i,)) threadd_list.append(t) t.setDaemon(True) #主程序结束子程序结束 t.start() #启动子进程 print('共用时%s' % (time.time() - d))
1开始 2开始 3开始 4开始 5开始 共用时0.0019898414611816406 #主程序共耗时0秒,主程序结束子程序也结束
当有多个子线程而只设置一个守护线程则没有意义,
因为其他子线程并没有运行完,所以主线程并没有真正意义上的运行完,只有所有的子线程都是守护线程才能在主线程运行完直接全部结束,
否则只要有一个子线程在运行都要继续等待.
import time import threading # 开多线程的模块 def c(a): print('%s开始' %(a)) if a == 5: #如果是5等待1秒 time.sleep(1) print('%s运行完成'% (a)) elif a == 1: #如果是1等待5秒 time.sleep(5) print('%s运行完成' % (a)) else: time.sleep(a) print('%s运行完成' % (a)) threadd_list = [] d = time.time() for i in range(1, 6): #循环开新的子进程 t = threading.Thread(target=c,args=(i,)) threadd_list.append(t) if i == 1: #只开启1的守护线程, t.setDaemon(True) t.start() #启动子进程 print('共用时%s' % (time.time() - d)) #共耗时 1开始 2开始 3开始 4开始 5开始 共用时0.0009970664978027344 5运行完成 2运行完成 3运行完成 4运行完成 #此处缺少1运行完成,因为当1运行完时之前其他所有子线程都运行结束了,也意味着主线程结束了,因此1与主线程一起结束了
GIL锁
由于GIL锁的存在,python不能并行,只能并发, 因此python不能算是真正意义上的多线程并行运算.,只能是占用一个cpu的一个线程的并发运算
因此,对于密集型运算,python不适用 threading多线程模块
密集型计算串行:
import time import threading # 开多线程的模块 def aa(): a = 1 for i in range(1,100000): a *= i def bb(): a = 1 for i in range(1,100000): a *= i c = time.time() aa() bb() print('耗时%s' %(time.time() - c)) 耗时5.802525758743286 #密集型并行计算总耗时5.8秒
密集型threading多线程计算 并发:
import time import threading # 开多线程的模块 def aa(): a = 1 for i in range(1,100000): a *= i def bb(): a = 1 for i in range(1,100000): a *= i c = time.time() t1 = threading.Thread(target=aa) t2 = threading.Thread(target=bb) t1.start() t2.start() t1.join() t2.join() print('耗时%s' %(time.time() - c)) 耗时6.302755832672119 #密集型threading多线程计算并发 耗时6.3秒 , 计算量越大差距越大
对于密集型计算的开启multiprocessing多进程会快 ,但是我们不可能无限量开进程
multiprocessing多进程模块
def aa(): a = 1 for i in range(1, 100000): a *= i def bb(): a = 1 for i in range(1, 100000): a *= i if __name__ == '__main__': #多进程模块必须这样写if判断,否则报错 import time import multiprocessing # 引用多进程模块 c = time.time() t1 = multiprocessing.Process(target=aa) #和多线程用法基本一致 t2 = multiprocessing.Process(target=bb) t1.start() t2.start() t1.join() #也有join和setDaemon()方法
t2.join()
print('耗时%s' %(time.time() - c))
耗时4.506261587142944