Python既支持多进程,又支持多线程
time
import time
time.sleep(1)#休眠当前线程1秒
time.time()#当前时间
多进程 multiprocessing
通常在计算密集型时使用多进程,会占用额外内存空间
os.fork()
只能在Mac和Linux下调用
需要跨平台应使用multiprocessing
多线程 threading
通常在IO密集型时使用多线程
-
Python中同一个进程下的多线程只能使用同一个CPU核(由于GIL锁)
-
通过
threading.current_thread()
可以查看当前所在的线程,主线程实例的名字叫MainThread,子线程的名字在创建时通过name
指定,未指定则自动命名为Thread-1
,Thread-2
……- 通过
threading.Thread()
创建线程,通过线程实例的start
方法开始线程 -
start
前调用setDaemon
设置此子线程是否为守护线程。
默认False
不回收,主线程执行完自己的任务后,子线程继续执行自己的任务。
设为True
相当于向主线程中注册守护,主线程结束时会将其一并回收 -
start
后调用join(timeout=None)
进行线程同步,在该子线程完成前会阻塞主线程后续代码执行。如有设置timeout,则最多只阻塞timeout的时间
- 通过
import threading
import time
def td():
time.sleep(1)
print('当前线程名字是:' + threading.current_thread().name)
time.sleep(1)
if __name__ == '__main__':
td()
s_time = time.time()
print(s_time)
print('这是主线程:' + threading.current_thread().name)
tdg_list = []
for i in range(5):
t = threading.Thread(target=td)
tdg_list.append(t)
for t in tdg_list:
# t.setDaemon(True)#设置守护线程(后台线程) 当前线程(主线程)结束时t线程也结束
t.start()
t.join()#在t完成前阻塞当前线程(主线程)
print('主线程结束了!', threading.current_thread().name)
print('一共用时:', time.time()-s_time)
- 多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,为了防止多个线程同时操作一个数据引起的错误,使用
threading.Lock()
保证某段代码每次只有一个线程在执行
只有一个线程能通过.acquire()
获取锁,并继续执行代码,其他线程调用.acquire()
时会继续等待直到锁被.release()
释放。
lock=threading.Lock()
lock.acquire()
try:#通过try...finally保证锁能被释放
change_it(n)
finally:
lock.release()
-
由于可以存在多个锁,不同的线程持有不同的锁,并试图获取对方持有的锁时,可能会造成死锁,导致多个线程全部挂起,既不能执行,也无法结束。
-
通过
threading.Event()
创建事件(标志默认为False)也可以用于阻塞线程,区别在于当event标志为True时所有阻塞同时畅通- event.wait(timeout=None):调用该方法的线程会被阻塞,如果设置了timeout参数,超时后,线程会停止阻塞继续执行;
- event.set():将event的标志设置为True,调用wait方法的所有线程将被唤醒;
- event.clear():将event的标志设置为False,调用wait方法的所有线程将被阻塞;
- event.isSet():判断event的标志是否为True。
import threading
import time
def do(event):
print('start')
event.wait()#红灯,所有线程执行都这里都在等待
print('end')
event_obj = threading.Event()#创建一个事件
for i in range(3):#创建10个线程
t= threading.Thread(target=do,args=(event_obj,))
t.start()
time.sleep(3)
event_obj.clear()#让灯变红,默认也是红的,阻塞所有线程运行
data= input('请输入要:')
print(event_obj.isSet())#False
if data =='True':
event_obj.set()#变绿灯
#start
#start
#start
#请输入要:True
#end
#end
#end
ThreadLocal
一个ThreadLocal实例虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰也不用管理锁的问题,ThreadLocal内部会处理。
local_school = threading.local()
local_school.student = name
print(local_school.student)
Master-Worker 模式
通常多任务的程序可以采用Master-Worker
模式,Master负责分配任务,Worker负责执行任务
- 如果用多进程实现Master-Worker,主进程就是Master,其他进程就是Worker。
这种方式比较稳定,一个子进程挂了不会影响其他进程,但在windows下开销特别大(因windows对多进程优化较差) - 如果用多线程实现Master-Worker,主线程就是Master,其他线程就是Worker。
比多进程快(尤其是windows),但是一个线程挂了会导致整个程序挂掉
计算密集型与IO密集型
-
计算密集型任务
特点是要进行大量的计算,消耗CPU资源,比如计算圆周率、对视频进行高清解码等等,全靠CPU的运算能力。任务越多,花在任务切换的时间就越多,CPU效率就越低,所以,计算密集型任务同时进行的数量不应大于CPU的核心数。
对于计算密集型任务,最好用C语言编写。 -
IO密集型
涉及到网络、磁盘IO的任务都是IO密集型任务CPU消耗很少,任务的大部分时间都在等待IO操作完成(因为IO的速度远远低于CPU和内存的速度)。对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。常见的大部分任务都是IO密集型任务,比如Web应用。
对于IO密集型任务,用运行速度极快的C语言替换用Python这样运行速度极低的脚本语言,对运行效率提升不大,开发效率却低,因此脚本语言更为合适。
分布式进程
把繁重任务分布到多台机器的环境下
网友评论