# 图片 下载耗时 用多线程 # threading模块 import threading import time def coding(): for i in range(3): print("正在写代码%s"%i) time.sleep(1) def drawing(): for i in range(3): print("正在画画%s"%i) time.sleep(1) def main(): # 创建一个子线程 t1 = threading.Thread(target=coding, ) t1.start() t2 = threading.Thread(target=drawing, ) t2.start() if __name__ == '__main__': main() ##################################### # 通过threading.current_thread() 获取当前的线程对象 def coding(): for i in range(3): print("正在写代码%s" % threading.current_thread()) time.sleep(1) def drawing(): for i in range(3): print("正在画画%s" % threading.current_thread()) time.sleep(1)
# 使用Thread类创建多线程 # 区别就是继承一下threading.Thread, 把之前的函数的代码写到 def run下面 import threading import time class CodingThread(threading.Thread): def run(self): for i in range(3): print("正在写代码%s"%threading.current_thread()) time.sleep(1) class DrawingThread(threading.Thread): def run(self): for i in range(3): print("正在画画%s"%threading.current_thread()) time.sleep(1) def main(): # 创建一个子线程 t1 = CodingThread() t1.start() t2 = DrawingThread() t2.start() if __name__ == '__main__': main()
# 多线程共享全局变量 和 锁的问题(修改全局变量的时候要加锁,访问全局变量的不用加锁) # 线程执行的顺序是无序的 import threading VALUE = 0 gLock = threading.Lock() # 创建锁 def add_value(): global VALUE gLock.acquire() # 加锁 for x in range(1000000): VALUE += 1 gLock.release() # 解锁 print("value,%d"%VALUE) def main(): for x in range(2): t = threading.Thread(target=add_value) t.start() if __name__ == '__main__': main() # 加锁后执行结果,就不会有错误了 # value,1000000 # value,2000000 # 不加锁的话两个线程会同时修改VALUE的数据, 相当于多线程同时对一个url 进行获取
# Lock版的 生产者或消费者模式, 一般都是在多线程中才使用 # 爬虫中生产者专门爬取url, 消费者专门解析url import threading import random import time gMoney = 1000 gLock = threading.Lock() class Producer(threading.Thread): def run(self): global gMoney while True: money = random.randint(100,1000) gLock.acquire() gMoney += money print("%s生产了%d的钱,现在总共有%d"%(threading.current_thread(),money,gMoney)) gLock.release() time.sleep(1) class Consumer(threading.Thread): def run(self): global gMoney while True: money = random.randint(100,1000) gLock.acquire() if gMoney >= money: gMoney -= money print("消费者%s,消费了%d,还剩有%d"%(threading.current_thread(),money,gMoney)) else: print("余额不足,当前金额是%d, 需要消费的金额是%d"%(gMoney,money)) gLock.release() time.sleep(1) def main(): for x in range(2): t = Producer(name="生产者%d"%x) t.start() for x in range(3): t = Consumer(name='消费者%d'%x) t.start() if __name__ == '__main__': main() # 以上是一个死循环模式 现在要求生产者只生产10次 就停止, # 消费者把已经生产出来的给消费掉, 消费完毕就停止消费 # 相当于爬虫爬取一定个数的 url 就停止 # 对上面代码修改 import threading import random import time gMoney = 1000 gLock = threading.Lock() gTimes = 0 gTotalTimes = 10 class Producer(threading.Thread): def run(self): global gMoney global gTimes global gTotalTimes while True: money = random.randint(100,1000) gLock.acquire() # if gTimes >= gTotalTimes: # gLock.release() # break # gMoney += money # gTimes += 1 # print("%s生产了%d的钱,现在总共有%d" % (threading.current_thread(), money, gMoney)) # gLock.release() # time.sleep(1) if gTimes < gTotalTimes: gMoney += money gTimes += 1 print("%s生产了%d的钱,现在总共有%d"%(threading.current_thread(),money,gMoney)) gLock.release() time.sleep(1) else: print("已经生产了10次, 停止生产") gLock.release() break class Consumer(threading.Thread): def run(self): global gMoney while True: money = random.randint(100,1000) gLock.acquire() if gMoney >= money: gMoney -= money print("消费者%s,消费了%d,还剩有%d"%(threading.current_thread(),money,gMoney)) else: if gTimes >= gTotalTimes: gLock.release() break print("余额不足,当前金额是%d, 需要消费的金额是%d"%(gMoney,money)) gLock.release() time.sleep(1) def main(): for x in range(2): t = Producer(name="生产者%d"%x) t.start() for x in range(3): t = Consumer(name='消费者%d'%x) t.start() if __name__ == '__main__': main() # 面试题中有问 怎么确保多线程爬取的数据不会错乱? 可以使用这个 生产者消费者模式 # 给多线程加锁之后, 保证同一个url 不会被爬取两次
# condition版的生产者与消费者模式 比lock版本优化了 # lock在while循环中 不断的加锁解锁, 消耗cup,所以不是最好的 # threading.Condition 继承threading.Lock, 它可以在修改全局数据的时候加锁,在修改完毕后解锁 # threading.Condition相关5个函数介绍: # acquire 上锁, # release 解锁 # wait 将当前线程处于等待状态(阻塞), 并且会释放锁,其他线程就可以使用锁了. # wait 函数可以被notify和notify_all函数唤醒, wait被唤醒后等待上锁, 上锁后继续执行下面代码 # notify 通知某个等待的线程,默认是第一个等待的线程.告诉等待的线程可以去获取锁了 # notify_all 通知所有正在等待的线程. notify和notify_all不会释放锁,需要在release之前使用 # threading.Condition 继承threading.Lock import threading import random import time gMoney = 1000 gCondition = threading.Condition() gTimes = 0 gTotalTimes = 10 class Producer(threading.Thread): def run(self): global gMoney global gTimes global gTotalTimes while True: money = random.randint(100,1000) gCondition.acquire() if gTimes >= gTotalTimes: gCondition.release() break gMoney += money gTimes += 1 print("%s生产了%d的钱,现在总共有%d" % (threading.current_thread(), money, gMoney)) gCondition.notify_all() #通知wait等待的线程 gCondition.release() time.sleep(1) class Consumer(threading.Thread): def run(self): global gMoney while True: money = random.randint(100,1000) gCondition.acquire() while gMoney < money: # 被等待的线程被唤醒后,又去线程队列中排队,但是排到的时候钱又不足了 if gTimes > gTotalTimes: gCondition.release() return #直接跳出两个 while 循环 print("%s,准备消费%d,剩余金额%d,金额不足,继续等待!!!" % (threading.current_thread, money, gMoney)) gCondition.wait() gMoney -= money print("消费者%s,消费了%d,剩余金额%d"%(threading.current_thread,money,gMoney)) gCondition.release() time.sleep(1) def main(): for x in range(2): t = Producer(name="生产者%d"%x) t.start() for x in range(3): t = Consumer(name='消费者%d'%x) t.start() if __name__ == '__main__': main()
# Queue线程安全队列 线程安全的时候就不用加锁了 ''' 在线程中,访问一些全局变量,加锁是一个经常的过程. 如果想把一些数据存储到某个队列中,那么python内置了一个线程安全的模块叫做queue模块. python中的queue模块提供了同步的,线程安全的队列类,包括FIFO先进先出队列Queue, LIFO后进先出队列LifeQueue. 这些队列实现了锁原理(可以理解为原子操作,要么都改,要么都不改),能够在多线程中直接使用,可以使用队列实现线程间的同步. 相关函数: 1.初始化 Queue(maxsize)创建一个先进先出的队列 2.qsize() 返回队列的大小,有多少个元素 3.empty() 判断队列是否为空 4.full() 判断队列是否满 5.get() 从队列中获取最后一个 6.put() 将一个数据放到队列中 q.get(block=True) 如果队列当中没有值就一直阻塞在这里,默认是True q.put(block=True) 表示如果给队列添加值,如果队列是满的就一直等待着 ''' from queue import Queue q = Queue(3) q.put(2) q.put(1) q.put(3) print(q.qsize()) # 3 print(q.full()) # true print(q.empty()) # false print(q.get()) # 2 ########################################## from queue import Queue import time import threading def set_value(q): index = 0 while True: q.put(index) index += 1 time.sleep(3) def get_value(q): while True: print(q.get()) def main(): q = Queue(4) t1 = threading.Thread(target=set_value,args=[q]) t2 = threading.Thread(target=get_value,args=[q]) t1.start() t2.start() if __name__ == '__main__': main() # set_value是每隔3秒产生一个 # get_value是一直在取,如果队列中没有就等着