设为首页 收藏本站
查看: 499|回复: 0

[经验分享] python线程的使用模式

[复制链接]

尚未签到

发表于 2015-12-2 10:36:39 | 显示全部楼层 |阅读模式
  为了解决阻塞(如I/O)问题,我们需要对程序进行并发设计。
  本文将通过将线程和队列 结合在一起,轻松地在 Python 中完成线程编程,创建一些简单但有效的线程使用模式。
  

一、使用线程
  先看一个线程不多的例子,不存在阻塞,很简单:



import threading
import datetime
class MyThread(threading.Thread):
def run(self):
now = datetime.datetime.now()
print("{} says Hello World at time: {}".format(self.getName(), now))
for i in range(2):
t = MyThread()
t.start()
      代码解读
  1. 两个线程都输出了 Hello World 语句,并都带有日期戳。
  2. 两个导入语句:一个导入了日期时间模块,另一个导入线程模块。
  3. 类 MyThread 继承自 threading.Thread,也正因为如此,您需要定义一个 run 方法,以此执行您在该线程中要运行的代码。
  4. run 方法中的self.getName() 是一个用于确定该线程名称的方法。
  5. 最后三行代码实际地调用该类,并启动线程。如果注意的话,那么会发现实际启动线程的是 t.start()。
  
  

二、使用线程队列
  如前所述,当多个线程需要共享数据或者资源的时候,可能会使得线程的使用变得复杂。线程模块提供了许多同步原语,包括信号量、条件变量、事件和锁。当这些 选项存在时,最佳实践是转而关注于使用队列。相比较而言,队列更容易处理,并且可以使得线程编程更加安全,因为它们能够有效地传送单个线程对资源的所有访 问,并支持更加清晰的、可读性更强的设计模式。
  在下一个示例中,我们的目的是:获取网站的 URL,并显示页面的前 300 个字节。
  先看看串行方式或者依次执行实现的代码:



from urllib import request
import time
hosts = ["http://yahoo.com", "http://amazon.com", "http://ibm.com", "http://apple.com"]
start = time.time()
#grabs urls of hosts and prints first 1024 bytes of page
for host in hosts:
url = request.urlopen(host)
print(url.read(200))
print("Elapsed Time: %s" % (time.time() - start))
      代码解读
  1. urllib 模块减少了获取 Web 页面的复杂程度。两次 time.time() 用于计算程序运行时间。
  2. 这个程序的执行速度是 13.7 秒,这个结果并不算太好,也不算太糟。
  3. 但如果需要检索数百个 Web 页面,那么按照这个平均值,总时间需要花费大约 1000 秒的时间。如果需要检索更多页面呢?
  
  
  下面给出线程化版本:



import queue
import threading
from urllib import request
import time
hosts = ["http://yahoo.com", "http://amazon.com", "http://ibm.com", "http://apple.com"]
in_queue = queue.Queue()
class ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, in_queue):
threading.Thread.__init__(self)
self.in_queue = in_queue
def run(self):
while True:
#grabs host from queue
host = self.in_queue.get()
#grabs urls of hosts and prints first 1024 bytes of page
url = request.urlopen(host)
print(url.read(200))
#signals to queue job is done
            self.in_queue.task_done()
start = time.time()
def main():
#spawn a pool of threads, and pass them queue instance
for i in range(4):
t = ThreadUrl(in_queue)
t.setDaemon(True)
t.start()
#populate queue with data   
for host in hosts:
in_queue.put(host)
#wait on the queue until everything has been processed     
    in_queue.join()
main()
print("Elapsed Time: %s" % (time.time() - start))
      代码解读
  1. 与第一个线程示例相比,它并没有复杂多少,因为使用了队列模块。
  2. 创建一个 queue.Queue() 的实例,然后使用数据对它进行填充。
  3. 将经过填充数据的实例传递给线程类,后者是通过继承 threading.Thread 的方式创建的。
  4. 生成守护线程池。
  5. 每次从队列中取出一个项目,并使用该线程中的数据和 run 方法以执行相应的工作。
  6. 在完成这项工作之后,使用 queue.task_done() 函数向任务已经完成的队列发送一个信号。
  7. 对队列执行 join 操作,实际上意味着等到队列为空,再退出主程序。
  在使用这个模式时需要注意一点:通过将守护线程设置为 true,将允许主线程或者程序仅在守护线程处于活动状态时才能够退出。这种方式创建了一种简单的方式以控制程序流程,因为在退出之前,您可以对队列执行 join 操作、或者等到队列为空。
  join()保持阻塞状态,直到处理了队列中的所有项目为止。在将一个项目添加到该队列时,未完成的任务的总数就会增加。当使用者线程调用 task_done() 以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。当未完成的任务的总数减少到零时,join() 就会结束阻塞状态。
  
  
  

三、使用多个队列
  下一个示例有两个队列。其中一个队列的各线程获取的完整 Web 页面,然后将结果放置到第二个队列中。然后,对加入到第二个队列中的另一个线程池进行设置,然后对 Web 页面执行相应的处理。
  提取所访问的每个页面的 title 标记,并将其打印输出。



import queue
import threading
from urllib import request
import time
from bs4 import BeautifulSoup
hosts = ["http://yahoo.com", "http://amazon.com", "http://ibm.com", "http://apple.com"]
in_queue = queue.Queue()
out_queue = queue.Queue()
class ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, in_queue, out_queue):
threading.Thread.__init__(self)
self.in_queue = in_queue
self.out_queue = out_queue
def run(self):
while True:
#grabs host from queue
host = self.in_queue.get()
#grabs urls of hosts and then grabs chunk of webpage
url = request.urlopen(host)
chunk = url.read()
#place chunk into out queue
            self.out_queue.put(chunk)
#signals to queue job is done
            self.in_queue.task_done()
class DatamineThread(threading.Thread):
"""Threaded Url Grab"""
def __init__(self, out_queue):
threading.Thread.__init__(self)
self.out_queue = out_queue
def run(self):
while True:
#grabs host from queue
chunk = self.out_queue.get()
#parse the chunk
soup = BeautifulSoup(chunk)
print(soup.findAll(['title']))
#signals to queue job is done
            self.out_queue.task_done()
start = time.time()
def main():
#spawn a pool of threads, and pass them queue instance
for i in range(4):
t = ThreadUrl(in_queue, out_queue)
t.setDaemon(True)
t.start()
#populate queue with data
for host in hosts:
in_queue.put(host)
for i in range(4):
dt = DatamineThread(out_queue)
dt.setDaemon(True)
dt.start()

#wait on the queue until everything has been processed
    in_queue.join()
out_queue.join()
main()
print("Elapsed Time: %s" % (time.time() - start))
      代码解读
  1. 我们添加了另一个队列实例,然后将该队列传递给第一个线程池类 ThreadURL。
  2. 对于另一个线程池类 DatamineThread, 几乎复制了完全相同的结构。
  3. 在这个类的 run 方法中,从队列中的各个线程获取 Web 页面、文本块,然后使用 Beautiful Soup 处理这个文本块。
  4. 使用 Beautiful Soup 提取每个页面的 title 标记、并将其打印输出。
  5. 可以很容易地将这个示例推广到一些更有价值的应用场景,因为您掌握了基本搜索引擎或者数据挖掘工具的核心内容。
  6. 一种思想是使用 Beautiful Soup 从每个页面中提取链接,然后按照它们进行导航。
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-146239-1-1.html 上篇帖子: python 优秀文章索引 下篇帖子: Python学习笔记(迭代、模块扩展、GUI 、编码处理等)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表