1、同步和异步
通常来说,程序都是顺序执行,同一时刻只会发生一件事。如果一个函数方法依赖于另一个函数方法的结果,它只能等待那个函数方法结束才能继续执行,从用户的角度来说,整个程序才算运行完毕。同步是指完成事务的逻辑,先执行第一个事务,如果阻塞了,会一直等待,直到这个事务完成,再执行第二个事务,顺序执行异步是和同步相对的,异步是指在处理调用这个事务的之后,不会等待这个事务的处理结果,直接处理第二个事务去了,通过状态、通知、回调来通知调用者处理结果。多线程和多进程都是通过异步的方式处理事物。
2、Python 多线程
多线程是一种并发编程方式,可以多个线程在同一进程中同时运行。每个线程都可以独立执行任务,但它们共享同一块内存空间。
线程也叫轻量级进程,是操作系统能够进行运算调度的最小单位,它被包涵在进程之中,是进程中的实际运作单位。线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
Python有GIL
全局解释器锁,同一时间只能有一个线程执行。则不能利用多核优势。如多线程进程是CPU密集型的,则多线程并不能带来效率上的提升,相反还可能会因线程的频繁切换,导致效率下降;如IO密集型,多线程进程可以利用IO阻塞等待时的空闲时间执行其他线程,则可以提升效率。
3、Python 多线程的使用
Python中使用多线程需要import threading
,具体使用如下,
import threading import time def listen_work(name): i=5 while i>0: time.sleep(1) print(name,"正在处理文件") i-=1 def download_work(name): i=3 while i>0: time.sleep(2) print(name,"正在下载文件") i-=1 if __name__ == '__main__': p1 = threading.Thread(target=listen_work,args=("多线程应用",)) p2 = threading.Thread(target=download_work,args=("多线程应用",)) p1.start() p2.start()
4、Python 多线程中的锁
多线程中锁的作用是为了保证数据的 一致性 ,对锁内的资源(变量)进行锁定,避免在读取时同时其它线程进行修改 。以达到我们的预期效果。
1)条件变量同步
Python提供了threading.Condition
对象用于条件变量线程的支持,除了提供RLock()
或Lock()
的方法外,还提供了 wait()
、notify()
、notifyAll()
方法。
方法 | 描述 |
wait() | 条件不满足时调用,线程会释放锁并进入等待阻塞; |
notify() | 条件创造后调用,通知等待池激活一个线程; |
notifyAll() | 条件创造后调用,通知等待池激活所有线程。 |
代码如下,
import threading, time from random import randint class Producer(threading.Thread): def run(self): global StoreList while True: val = randint(0, 100) print('生产者', self.name, ':Append'+str(val),StoreList) if lock_con.acquire(): StoreList.append(val) lock_con.notify() lock_con.release() time.sleep(3) class Consumer(threading.Thread): def run(self): global StoreList while True: lock_con.acquire() if len(StoreList) == 0: lock_con.wait() print('消费者', self.name, ":Delete" + str(StoreList[0]), StoreList) del StoreList[0] lock_con.release() time.sleep(0.25) if __name__ == "__main__": StoreList = [] lock_con = threading.Condition() threads = [] for i in range(5): threads.append(Producer()) threads.append(Consumer()) for t in threads: t.start() for t in threads: t.join() print('---- end ----')
2)条件同步
条件同步用于不同时访问共享资源的条件环境。
方法 | 描述 |
event.isSet() | 返回event的状态值; |
event.wait() | 如果 event.isSet()==False将阻塞线程; |
event.set() | 设置event的状态值为True, 所有阻塞池的线程激活进入就绪状态, 等待操作系统调度; |
event.clear() | 恢复event的状态值为False。 |
代码如下,
import threading, time class Master(threading.Thread): def run(self): print("Master: 开始执行") event.isSet() or event.set() time.sleep(5) print("Master: 执行完成") event.isSet() or event.set() class Worker(threading.Thread): def run(self): event.wait() print("Worker: 正在执行") time.sleep(0.25) event.clear() event.wait() print("Worker: 任务完成!") if __name__ == "__main__": event = threading.Event() threads = [] for i in range(5): threads.append(Worker()) threads.append(Master()) for t in threads: t.start() for t in threads: t.join()
3)同步队列
Python中的Queue
对象也提供了对线程同步的支持。使用Queue
对象可以实现多个生产者和多个消费者形成的FIFO的队列。Queue.Queue
类即是一个队列的同步实现。队列长度可为无限或者有限。
put()
方法在队尾插入一个项目。put()
有两个参数,第一个item为必需的,为插入项目的值;第二个block
为可选参数,默认为1
。如果队列当前为空且block
为1
,put()
方法就使调用线程暂停,直到空出一个数据单元。如果block
为0
,put()
方法将引发Full
异常。
q.get([block[, timeout]])
方法从队头删除并返回一个项目。可选参数为block
,默认为True
。如果队列为空且block
为True
,get()
就使调用线程暂停,直至有项目可用。如果队列为空且block
为False
,队列将引发Empty
异常,timeout
等待时间。
方法 | 描述 |
qsize() | 返回队列的大小 |
empty() | 如果队列为空,返回True,反之False |
full() | 如果队列满了,返回True,反之False。与 maxsize 大小对应 |
get_nowait() | 相当q.get(False) |
put_nowait(item) | 相当q.put(item, False) |
task_done() | 在完成一项工作之后, q.task_done() 函数向任务已经完成的队列发送一个信号 |
join() | 实际上意味着等到队列为空,再执行别的操作 |
import threading, queue from time import sleep from random import randint class Production(threading.Thread): def run(self): i=5 while i>0: r = randint(0, 100) q.put(r) print("生产者生产: %s" %r) sleep(1) i-=1 class Proces(threading.Thread): def run(self): while not q.empty(): re = q.get() print('消费者消费: %s' %re) sleep(0.5) if __name__ == '__main__': q = queue.Queue(10) threads = [Production(),Production(),Proces()] for t in threads: t.start()
注意:多线程使用锁时,要防止死锁的产生,死锁的原因有多种,但是本质就是对资源不合理的竞争锁导致的。两个线程就陷入了相互等待的局面就是死锁。