为了解决阻塞(如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. SPA SEO thought

    angular, vue,ember,backbone等javascript framework大大方便了现代web开发,带来了用户体验的巨大提高,但是同时带来了另一个问题:SEO,由于搜索引擎无法运 ...

  2. iOS7中修改StatusBar的显示颜色

    iOS7中修改StatusBar的显示颜色 效果图如下: 在iOS7中想手动修改statusBar的颜色,第一步需要做的就是在plist文件中设置View controller-based statu ...

  3. swift版的枚举变量

    swift版的枚举变量 swift的枚举类型跟普通的类是极为类似的,使用的时候,请不要以为他是一个常量,以下是测试用源码 // // ViewController.swift // SwiftEnum ...

  4. 利用Django实现RESTful API(一)

    RESTful API现在很流行,这里是它的介绍 理解RESTful架构和 RESTful API设计指南.按照Django的常规方法当然也可以实现REST,但有一种更快捷.强大的方法,那就是 Dja ...

  5. C++项目规范

    https://segmentfault.com/a/1190000007659754

  6. Linux echo命令详解

    echo :输出文字到控制台 -n: 不换行输出 -e:解析转移字符   (-b: 退格  -n 换行 -t 空格) 常用的命令展示 echo {1..4} ==> seq -s " ...

  7. 【Pygame】 简易五子棋

    import pygame import sys import time import random pygame.init() screen_size = (800,560) WIDTH = 720 ...

  8. September 18th 2017 Week 38th Monday

    The only person you should try to be better than is the person you were yesterday. 你唯一应该试着去超越的人,是昨天的 ...

  9. MongoDB 多实例安装成服务

    转发自:https://www.cnblogs.com/GainLoss/p/6906937.html 1.在mongodb的官网上下载安装包 https://www.mongodb.com/down ...

  10. THE CUP OF LIFE即生命之杯。

    生命之杯 编辑 THE CUP OF LIFE即生命之杯. <生命之杯>(西班牙语:La copa de la vida,英语:The Cup of Life)是一首由波多黎各裔歌手瑞奇· ...