由于GIL的存在,Python的多线程实际上是伪多线程,在同一时刻只有一个线程。因此Python的多线程适用于IO密集型任务(文件读写,网络请求等),至于计算密集型任务考虑使用多进程。
多线程
直接调用
1
2
3
4
5
6
|
import threading
def run(i):
print(i)
thread = threading.Thread(target=run, args=[1,])
thread.start()
|
继承调用
1
2
3
4
5
6
7
|
class MyThread(threading.Thread):
def __init__(self):
super().__init__()
def run(self):
# run方法必须实现,这里放置自定义的内容
pass
|
阻塞和守护线程
thread.join()方法在该线程对象启动了之后调用线程的join()方法之后,那么主线程将会阻塞在当前位置直到子线程执行完成才继续往下走,如果所有子线程对象都调用了join()方法,那么主线程将会在等待所有子线程都执行完之后再往下执行。
setDaemon(True)方法在子线程对象调用start()方法(启动该线程)之前就调用的话,将会将该子线程设置成守护模式启动。当子线程还在运行的时候,父线程已经执行完了,如果这个子线程设置是以守护模式启动的,那么随着主线程执行完成退出时,子线程立马也退出,如果没有设置守护启动子线程(也就是正常情况下)的话,主线程执行完成之后,进程会等待所有子线程执行完成之后才退出。
线程锁
由于多线程共享同一块内存空间,可以访问其中的变量。
1
2
3
4
5
6
7
8
9
|
lock = threading.Lock()
lock.aquire()
# do something
lock.release()
# or using with, 锁会自动释放
with lock:
# do something
|
死锁发生的情况:
1)叠加锁:连续调用同一把锁
1
2
3
4
|
lock.aquire()
lock.aquire()
lock.release()
lock.release()
|
python引入RLock来解决这个问题。
2)相互调用锁
信号量
1
2
3
|
sem = threading.Semaphore(4)
# 只有4把锁
|
线程池
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from concurrent.future import ThreadPoolExeutor
p = ThreadPoolExecutor(max_workers=10)
future = p.submit(fn, args)
data = future.result()
deal_with(data)
# or async deal with data
p.submit(fn, args).add_done_callback(deal_with)
future_list = p.map(fn, *iterables)
p.shutdown()
|
对于进程池,只需将ThreadPoolExecuator改为ProcessPoolExecuator即可。