用mvc做网站的框架,个人网站推广方法,电子商务网页设计试题,简单的html网页制作当我们写的程序需要并发时#xff0c;我们就需要用到 Python 中的一些并发库#xff0c;例如 asyncio、thread、 multiprocessing 等#xff0c;本文主要介绍 Python 标准库中的多线程库 threadthreading 基本使用使用多线程的优势在于程序运行更快适用于 IO 密集的场景Pyth…当我们写的程序需要并发时我们就需要用到 Python 中的一些并发库例如 asyncio、thread、 multiprocessing 等本文主要介绍 Python 标准库中的多线程库 threadthreading 基本使用使用多线程的优势在于程序运行更快适用于 IO 密集的场景Python 标准库提供了两个模块_thread 和 threading threading 对 _thread 进行了封装虽然 Python 有 GIL 会在线程切换时消耗很多资源但是在 IO 密集的场景下Python 多线程还是很管用的先看看 threading 的基本使用import threadingdef hello(*args, **kwargs):#定义一个 hello 函数print(hello, world, args, kwargs)实例化线程对象target 参数为指定函数args 是传递的列表参数kwargs 是传递的字典参数通过 start 方法启动一个线程t threading.Thread(targethello, args[1, 2, 3], kwargs{a: b})t.start()hello, world (1, 2, 3) {a: b}threading 和 Thread 常用参数和方法name 参数导入 logging 库更加直观的显示线程的信息import logginglogging.basicConfig(levellogging.DEBUG, format%(asctime)s %(levelname)s [%(threadName)s] %(message)s) #配置 loggingdef hello():logging.info(test)t threading.Thread(targethello, namehello_thread)#指定线程名称2017-03-12 22:30:58,556 INFO [hello_thread] testdaemon线程退出的时候该线程的所有daemon 的子线程都会退出而no-daemon 子线程不会退出而线程退出会等待所有的 no-daemon 子线程退出join 方法子线程的 join 方法会阻塞主线程直到子线程退出或者超时如果不设置超时时间就会一直等待子线程退出import threadingfrom time import sleepdef worker():sleep(3)threads [threading.Thread(targetworker) for _ in range(3)]def main():for t in threads:t.start()for t in threads:t.join()执行 main 函数能够感觉到是一直阻塞着的直到子线程退出main()enumerate 方法列出当前所有的线程threading.enumerate()[_mainthread started,local线程共享内存、状态和资源但是threading local 对象的属性只对当前线程可见ctx threading.local()ctxctx.data aaadef worker():print(ctx.data)worker()aaathreading.Thread(targetworker).start()In [101]: Exception in thread Thread-2477:Traceback (most recent call last):File /usr/local/opt/pyenv/versions/3.5.3/Python.framework/Versions/3.5/lib/python3.5/threading.py, line 914, in _bootstrap_innerself.run()File /usr/local/opt/pyenv/versions/3.5.3/Python.framework/Versions/3.5/lib/python3.5/threading.py, line 862, in runself._target(*self._args, **self._kwargs)File , line 2, in workerprint(ctx.data)AttributeError: _thread._local object has no attribute data实例化 Thread 类之前通过 target 参数的方式不是非常的优雅其实可以通过继承 Thread 类并重写 run 方法来编写更加优雅的代码class MyThread(threading.Thread):def run(self):print(hello, world)MyThread()MyThread().start()hello, world传递参数通过重写 __init__() 方法传递参数class MyThread(threading.Thread):def __init__(self, *args, **kwargs):super().__init__()self.args argsself.kwargs kwargsdef run(self):print(hello, world, self.args, self.kwargs)t MyThread(1, 2, 3, stateok)t.start()hello, world (1, 2, 3) {state: ok}线程同步在使用多个线程同时操作一个资源的情况下( 例如读文件) 我们需要控制同一时刻只有一个线程对资源进行操作这时候就需要一些同步机制如 锁、队列、条件、事件等Lock我们可以通过 threading.Lock 来解决这个问题Lock 对象一般有两个操作获取 acquire 和 释放 release通过 acquire 方法 将 Lock 对象状态设置为锁定如果是锁定状态则会阻塞release 方法则将 Lock 对象解锁import threadinglock threading.Lock()lock.acquire()Truelock.acquire(timeout3)Falselock.release()lock.acquire(timeout3)True一个抓取页面的例子通过使用锁实现了线程之间的同步import requestsimport threadingimport logginglogging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s [%(threadName)s] %(message)s)lock threading.Lock()file open(data, a)urls [http://baidu.com, http://cn.bing.com/]class FetchUrls(threading.Thread):def __init__(self, url: str, file, lock: threading.Lock, nameNone):super().__init__()self.url urlself.file fileself.lock lockif name is not None:self.name namedef run(self):res requests.get(self.url)self.lock.acquire()logging.info(Lock Acquire)self.file.write(res.text)logging.info(File Writed)self.lock.release()测试ts [FetchUrls(url, file, lock, nameurl) for url in urls][t.start() for t in ts]2017-03-13 14:00:05,142 INFO [http://baidu.com] Lock Acquire2017-03-13 14:00:05,142 INFO [http://baidu.com] File Writed2017-03-13 14:00:05,271 INFO [http://cn.bing.com/] Lock Acquire2017-03-13 14:00:05,272 INFO [http://cn.bing.com/] File WritedRLockRLock 是一个可重用锁可以多次调用 acquire 而不阻塞但是 release 时也要执行和 acquire 一样的次数import threadingrlock threading.RLock()rlock.acquire()Truerlock.acquire()Truerlock.acquire()Truerlock.release()rlock.release()rlock.release()rlock.release()Traceback (most recent call last):File , line 1, in RuntimeError: cannot release un-acquired lockCondition如果多个线程使用 生产者 — 消费者的模式可以使用 Condition生产者生产数据后通过 notify/notify_all 通知给消费者消费数据import threadingimport randomimport loggingimport timelogging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s [%(threadName)s] %(message)s)class Producer(threading.Thread):def __init__(self, datas: list, cond: threading.Condition, nameNone):super().__init__()self.cond condself.datas datasif name is not None:self.name namedef run(self):while True:data random.randint(1, 100)logging.info(data)self.datas.append(data)self.cond.acquire()self.cond.notify()self.cond.release()time.sleep(1)self.cond.acquire()self.cond.notify()self.cond.release()等价于with self.cond:self.notify()无论 notify 还是 wait 都需要先 acquire然后再 release一般使用 with 语句class Consumer(threading.Thread):def __init__(self, datas: list, cond: threading.Condition, nameNone):super().__init__()self.cond condself.datas datasif name is not None:self.name namedef run(self):while True:self.cond.acquire()while True:data self.datas.pop()logging.info(data)breakself.cond.wait() #消费者通过 wait 方法等待 生产者 notifyself.cond.release()def runner():datas []cond threading.Condition()t1 Producer(datas, cond, nameproducer)t2 Consumer(datas, cond, nameconsumer)t1.start()t2.start()t1.join()t2.join()测试runner()2017-03-13 14:56:12,442 INFO [producer] 892017-03-13 14:56:12,442 INFO [consumer] 892017-03-13 14:56:13,445 INFO [producer] 852017-03-13 14:56:13,445 INFO [consumer] 852017-03-13 14:56:14,450 INFO [producer] 572017-03-13 14:56:14,450 INFO [consumer] 572017-03-13 14:56:15,454 INFO [producer] 652017-03-13 14:56:15,454 INFO [consumer] 652017-03-13 14:56:16,458 INFO [producer] 152017-03-13 14:56:16,459 INFO [consumer] 15EventEvent 是一个简单的机制线程发出一个信号其他线程等待import threadingimport loggingimport timeimport randomlogging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s [%(threadName)s] %(message)s)class EventProducer(threading.Thread):def __init__(self, datas: list, event: threading.Event, nameNone):super().__init__()self.datas datasself.event eventif name is not None:self.name namedef run(self):while True:data random.randint(1, 100)logging.info(data)self.datas.append(data)self.event.set()time.sleep(1)class EventConsumer(threading.Thread):def __init__(self, datas: list, event: threading.Event, nameNone):super().__init__()self.datas datasself.event eventif name is not None:self.name namedef run(self):while True:self.event.wait() # wait 方法阻塞 消费者线程try:data self.datas.pop()logging.info(data)except IndexError:continuedef runner():event threading.Event()datas []t1 EventProducer(datas, event, nameEventProducer)t2 EventConsumer(datas, event, nameEventConsumer)t1.start()t2.start()t1.join()t2.join()测试runner()2017-03-13 16:18:54,251 INFO [EventProducer] 822017-03-13 16:18:54,251 INFO [EventConsumer] 822017-03-13 16:18:55,261 INFO [EventProducer] 372017-03-13 16:18:55,261 INFO [EventConsumer] 372017-03-13 16:18:56,270 INFO [EventProducer] 922017-03-13 16:18:56,271 INFO [EventConsumer] 92Queue之前的几个 提供者 — 消费者 的例子 一直用一个全局的列表来传递数据其实不是很科学不同线程传递数据应该使用 Queue 因为 Queue 本身也可以阻塞线程使用 Queue 还可以省去同步import queueimport threadingimport loggingimport randomfrom time import sleeplogging.basicConfig(levellogging.INFO, format%(asctime)s %(levelname)s [%(threadName)s] %(message)s)class QueueProducer(threading.Thread):def __init__(self, queue: queue.Queue(), nameNone):super().__init__()self.queue queueif name is not None:self.name namedef run(self):while True:data random.randint(1, 100)logging.info(data)self.queue.put(data)sleep(1)class QueueConsumer(threading.Thread):def __init__(self, queue: queue.Queue, nameNone):super().__init__()self.queue queueif name is not None:self.name namedef run(self):while True:data self.queue.get()logging.info(data)def runner():q queue.Queue()t1 QueueProducer(q, nameQueueProducer)t2 QueueConsumer(q, nameQueueConsumer)t1.start()t2.start()t1.join()t2.join()测试runner()2017-03-13 16:34:49,401 INFO [QueueProducer] 822017-03-13 16:34:49,401 INFO [QueueConsumer] 822017-03-13 16:34:50,405 INFO [QueueProducer] 22017-03-13 16:34:50,405 INFO [QueueConsumer] 22017-03-13 16:34:51,406 INFO [QueueProducer] 74GIL提到 Python 多线程就一定要说说 GIL Global Interpreter Lock 全局解释器锁由于 GIL 的存在Python 的线程不能达到真正的并行在 CPython (C语言实现的 Python) 中 线程使用的是操作系统原生的线程CPython 中一个解释器有一条主线程和若干条用户程序的线程由于 GIL 的存在每一个进程要执行时都要去获取 GIL 所以并不能有效的利用多核 CPU 实现多线程并行也就是说多个线程不能够同时执行如果要实现真正的并行就需要使用 multiprocessing 这个多进程模块了