为了解决阻塞(如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 从每个页面中提取链接,然后按照它们进行导航。

python线程的使用模式的更多相关文章

  1. Python Socket单线程+阻塞模式

    Python之旅]第五篇(二):Python Socket单线程+阻塞模式 python Socket单线程 Socket阻塞模式 串行发送 摘要:  前面第五篇(一)中的一个Socket例子其实就是 ...

  2. Python 线程和进程和协程总结

    Python 线程和进程和协程总结 线程和进程和协程 进程 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位: 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其 ...

  3. python——线程相关

    使用python的threading中的Thread 下面是两种基本的实现线程的方式: 第一种方式———— #coding=utf-8 """ thread的第一种声明及 ...

  4. python线程入门

    目录 python线程入门 线程与进程 线程 总结 参考 python线程入门 正常情况下,我们在启动一个程序的时候.这个程序会先启动一个进程,启动之后这个进程会启动起来一个线程.这个线程再去处理事务 ...

  5. python线程条件变量Condition(31)

    对于线程与线程之间的交互我们在前面的文章已经介绍了 python 互斥锁Lock / python事件Event , 今天继续介绍一种线程交互方式 – 线程条件变量Condition. 一.线程条件变 ...

  6. 5分钟看懂系列:Python 线程池原理及实现

    概述 传统多线程方案会使用"即时创建, 即时销毁"的策略.尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器 ...

  7. python 线程理解

    简介 一个应用程序由多个进程组成,一个进程有多个线程,一个线程则是操作系统调度的最小单位,当应用程序运行时,操作系统根据优先级和时间片调度线程(决定此时此刻执行哪个线程). python的线程 pyt ...

  8. Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解.[size=1.8em]Handler+Runna ...

  9. python——线程与多线程进阶

    之前我们已经学会如何在代码块中创建新的线程去执行我们要同步执行的多个任务,但是线程的世界远不止如此.接下来,我们要介绍的是整个threading模块.threading基于Java的线程模型设计.锁( ...

随机推荐

  1. Mysql性能监控项及sql语句

    推荐一款mysql监控软件MONyog 1.查询缓存: mysql> show variables like '%query_cache%'; 2.缓存在Cache中线程数量thread_cac ...

  2. MySQL->>innodb_autoinc_lock_mode参数控制auto_increment 插入数据时相关锁的模式

    转自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/15498/viewspace-2141640/ ---------------------------------- ...

  3. Oracle案例12——NBU Oracle恢复

    最近在做NBU ORACLE备份的恢复测试,执行恢复时报错ORA-27211: Failed to load Media Management Library,具体处理过程如下:一.错误信息 执行命令 ...

  4. 能力成熟度模型(CMM)

    能力等级 特点 关键过程 第一级 基本级 软件过程是混乱无序的,对过程几乎没有定义,成功依靠的是个人的才能和经验,管理方式属于反应式   第二级 重复级 建立了基本的项目管理来跟踪进度.费用和功能特征 ...

  5. python文本文件处理和用户输入

    #用户输入 a = input('please input: ') #这个输入什么即是什么,比如输入1,则a变量=1,输入'abc',则a变量 = 'abc',输入abc则报错,因为会把abc当做一个 ...

  6. 如何快速的给你的项目添加icon图标

    如何快速的给你的项目添加icon图标 下载软件 如何制作图片 将制作的图标拖到项目当中 设置启动页 注意: 如果手动添加了启动页的话,记得将Launch Screen中的东西清除掉

  7. 铁乐学python_day01-和python有关的唠嗑

    铁乐学python_day01-和python有关的唠嗑 文:铁乐与猫 2018-03-16 01_python的历史 python的创始人为荷兰人吉多·范罗苏姆(Guido van Rossum). ...

  8. matlab规定小数点保留4位且非科学计数法格式存储txt

    matlab 不保存为科学计数法 http://blog.sciencenet.cn/blog-472136-402727.html 经常在表示matlab值时,它总会把一些小于1的大于1000的数使 ...

  9. handsontable 和 echarts都定义了require方法,初始化时冲突了,怎么办?

    echarts初始化时报这个错误. require.config is not a function  方案一: 让其中一方的初始化不依赖于 require即可 1.去掉 var testDrowEc ...

  10. JS日期比较大小 给定时间和持续时间计算最终时间

       /* 往指定时间字符串上加时间间隔,获得新的时间字符串  * startDateStr:开始时间字符串,类似"2015-7-20 17:26:00"  * durationN ...