python3-多线程
多线程简介
线程(Thread)也称轻量级进程,时操作系统能够进行运算调度的最小单位,它被包涵在进程之中,时进程中的实际运作单位。线程自身不拥有资源,只拥有一些在运行中必不可少的资源,但他可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。
线程有就绪/阻塞/运行三种基本状态:
1/ 就绪状态是指线程具备运行的所有条件,逻辑上可以运行,在等待处理机
2 / 运行状态是指线程占有处理机正在运行。
3/ 阻塞状态是指线程在等待一个事件(如某个信号量),逻辑上不可执行
结论:在python中,对于计算密集型任务,多进程占优势;对于I/O密集型任务,多线程占优势
python多线程其它介绍
1/1 多线程threading模块
方法一 创建threading.Thread类的实例,调用其start()方法
方法二 继承Thread类,在子类中重写run()和init()方法
1/2 多线程同步Lock(互诉锁)
#如果多个线程共同对某个数据修改,则可能出现不可预料的结果,这个时候就需要使用互诉锁来进行同步
#调用锁
lock = threading.Lock()
#获取锁,用户线程同步
lock.acquire()
1/3 多线程同步Semaphore(信号量)
互诉锁是只允许一个线程访问共享数据,而信号量是同时允许一定数量的线程访问共享数据
semaphore = threading.BoundedSemaphore()
练习:
import threading
import time
semaphore = threading.BoundedSemaphore(5)
def yewubanli(name):
semaphore.acquire()
time.sleep(3)
print(f"{time.strftime('%Y-%m-%d %H:%M:%S')} {name} 正在办理业务")
semaphore.release()
thread_list = []
for i in range(12):
t = threading.Thread(target=yewubanli, args=(i,))
thread_list.append(t)
for thread in thread_list:
thread.start()
for thread in thread_list:
thread.join()
1/4 多线程同步Condition
条件对象Condition能让一个线程A停下来,等待其它线程B,线程B满足了某个条件后通知(notify)线程A继续运行。线程首先获取一个条件变量锁,如果条件不足,则该线程等待(wait)并释放条件变量锁;如果条件满足,就继续执行线程,执行完成后可以通知(notify)其它状态为wait的线程执行。其它处于wait状态的线程接到通知后会重新判断条件以确定是否继续执行。
cond = threading.Condition()
1/5 多线程同步Event
事件用于线程之间的通信。一个线程发出一个信号,其它一个或多个线程等待,调用Event对象的wait方法,线程则会阻塞等待,直到别的线程set之后才会被唤醒
cond = threading.Event()
1/6 线程优先级队列(queue)
Python的queue模块中提供了同步的/线程安全的队列类,包括先进先出队列Queue/后进后出队列LifoQueue和优先级队列PriorityQueue。这些队列都实现了锁原语,可以直接使用来实现线程之间的同步。
练习:
import threading, time
import queue
q = queue.Queue(maxsize=5)
def ProducerA():
count = 1
while True:
q.put(f"冷饮 {count}")
print(f"{time.strftime('%H:%M:%S')} A 放入:[冷饮 {count}]")
count += 1
time.sleep(1)
def ConsumerB():
while True:
print(f"{time.strftime('%H:%M:%S')} B 取出 [{q.get()}]")
time.sleep(5)
p = threading.Thread(target=ProducerA)
c = threading.Thread(target=ConsumerB)
c.start()
p.start()
1/7 多线程之线程池pool
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或其它更多资源。虚拟机也将视图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。同样的道理,多任务情况下每次都会生成一个新线程,执行任务后资源再被回收就显得非常低效,因此线程池就是解决这个问题的方法。类似的还有连接池/进程池等。
将任务添加到线程池中,线程池会自动指定一个空闲的线程取执行任务,当超过线程池的最大线程数时,任务需要等待有新的空闲线程后才会被执行。
我们可以使用threading模块及queue模块定制线程池,也可以使用multiprocessing。from multiprocessing import Pool这样导入的Pool表示的时进程池,from multiprocessing.dummy import Pool这样导入的Pool表示的时线程池
练习:
from multiprocessing.dummy import Pool as ThreadPool
import time
def fun(n):
time.sleep(2)
start = time.time()
for i in range(5):
fun(i)
print("单线程顺序执行耗时:", time.time() - start)
start2 = time.time()
pool = ThreadPool(processes=5)
results2 = pool.map(fun, range(5))
pool.close()
pool.join()
print("线程池(5)并发执行耗时:",time.time() - start2)
总结:
Python多线程适合用再I/O密集型任务中。I/O密集型任务较小时间用再CPU计算上,较多时间用再I/O上,如文件读写/web请求/数据库请求等;而对于计算密集型任务,应该使用多进程