Python的线程受限制于GIL锁,所以在处理大量运算时不占优势。Cpython解释器的原因。但是日常所用多数属于IO密集型程序,开销要比进程小的多,而且速度快。
Threading模块中除了常用的Thread类还有一些方法:
- threading.current_thread()方法:用来表明线程名的方法
- threading.get_ident() 方法:用来获取线程ID的方法
- threading.active_count()方法: 用来获取当前活跃的线程数量
- threading.enumerate() 方法:将线程名字和ID用列表的方式显示出来
关于守护线程
- 守护进程会随主进程结束而结束,防止出现僵尸进程。而守护线程则不会随着主线程结束而结束,进程会保留资源留给线程。
线程锁
-
同进程锁一样,线程中同样有数据安全问题,为了不让多个线程对一个数据进行操作,使用线程锁可以保证数据安全。
-
死锁的问题同样存在,使用递归锁RLock()可以解决死锁问题。在多个锁,锁一个数据时,互斥锁Lock()会产生死锁,而RLock()会保证在多个锁有一个在使用时,即使另外一个释放了,其他线程也不会获得锁。
-
因为线程之间数据共享,所以使用公共数据时应该加锁。
信号量
-
同一时间只有N个线程可以访问同一个短代码、数据
import threading import time def func(sem, a, b): sem.acquire() time.sleep(1) print(a+b) sem.release() sem = threading.Semaphore(4) for i in range(10): t = threading.Thread(target=func, args=(sem, i, i+5)) t.start()
事件
- 事件在创建时,自身带有False状态,默认wait()堵塞,clear()设置堵塞,set()清除堵塞。线程的Condition模块中,有acquire()、wait()、release()、notify(int x) 几个方法,wait会等待notify中传入的数字进行执行。wait()在线程函数内部,而notify在外部传入数值,两段代码必须分别用acquire和release包围。
定时器Timer()
-
利用定时器可以定时开启一个线程任务。
def func(): print("执行定时函数") threading.Timer(5.4, func).start()
-
如果想循环调用一个定是函数,将调用的线程加入到执行函数中即可
def func(): print("执行定时函数") threading.Timer(5.4, func).start() threading.Timer(5.4, func).start()
队列 queue
-
线程中使用的数据都可以共享,当出现数据安全问题时,为了保证线程数据安全,线程中也可以使用队列,这个队列并不在threading模块中,直接导入queue即可:
import queue q = queue.Queue() q.put() q.put_nowait() # 会报错,需要加入异常处理 q.get() q.get_nowait() # 会报错,需要加入异常处理
-
queue.LifoQueue() #栈,先进后出
-
queue.PriorityQueue() # 优先级队列
q = queue.PriorityQueue() q.put((20, "a")) q.put((10, "b")) print(q.get()) # 当优先级一样时,会根据数据的Ascii码排列先出顺序
线程池
使用python中新模块,concurrent中的线程池,同样也有进程池
import time
from concurrent.futures import ThreadPoolExecutor
def func(n):
time.sleep(2)
print(n)
tpool = ThreadPoolExecutor(max_workers=5)
for i in range(20):
tpool.submit(func, i)
-
shutdown() 可以实现 join 和 close 效果
-
result() 可以取得返回的结果
import time from concurrent.futures import ThreadPoolExecutor def func(n): time.sleep(2) print(n) return n*n tpool = ThreadPoolExecutor(max_workers=5) t_list = [] for i in range(20): t = tpool.submit(func, i) # 取结果 t_list.append(t) tpool.shutdown() # 等待所有线程执行完,并回收资源后继续执行主线程 print("主进程") for t in t_list: print(t.result()) # 打印结果,使用result()取出结果
-
在取得结果时,可以不用等待全部线程执行完,利用主线程取得结果即可。
回调函数
- 当线程有返回值时,可以利用回调函数进行再次处理:
import time
from concurrent.futures import ThreadPoolExecutor
def func(n):
time.sleep(2)
print(n)
return n*n
def call_back(n):
print("执行的结果是:", n.result()) # 这里传入的时一个对象,需要进行result
tpool = ThreadPoolExecutor(max_workers=5)
for i in range(20):
tpool.submit(func, i).add_done_callback(call_back)
# tpool.shutdown() # 等待所有线程执行完,并回收资源后继续执行主线程
print("主进程")
网友评论