使用Python进行线程编程

对于Python来说,并不缺少并发选项,其标准库包括了对线程、进程和异步I/O的支持。在许多情况下,通过创建诸如异步、线程和子进程之类的高层模块,Python简化了各种并发方法的使用。除了标准库之外,还有一些第三方的解决方案。例如Twisted、Stackless和进程Module。因为GIL,CPU受限的应用程序无法从线程中受益。使用Python时,建议使用进程,或者混合创建进程和线程。

首先弄清楚进程和线程的区别。线程和进程的不同之处在于,它们共享状态、内存和资源。对于线程来说,这个简单的区别既是它的优势,又是它的缺点。一方面,线程是轻量级的,并且相互之间易于通信,但另一方面,它们也带来了包括死锁、争用条件和好复杂性在内的各种问题。幸运的是,由于GIL和队列模块,与采用其他的语言相比,采用Python语言在线程实现的复杂性上要低的多。

一个简单的demo:

#!/usr/bin/env python
import Queue
import threading
import urllib2
import time

hosts = ["http://www.baidu.com", "http://www.sina.com.cn", "http://www.letv.com"]

class ThreadUrl(threading.Thread):
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue
        
    def run(self):
        while True:
            host = self.queue.get()
            url = urllib2.urlopen(host)
            print url.read(1024)
            self.queue.task_done()
            
def main():
    for i in range(5):
        t = ThreadUrl(queue)
        t.setDaemon(True)
        t.start()
    
    for host in hosts:
        queue.put(host)
    
    queue.join()

if __name__ == "__main__":
    start = time.time()
    main()
    print "Elapsed Time:%s" % (time.time() - start)

在Python中使用线程时,这个模型是一种很常见的并且推荐使用的方式。具体工作步骤描述如下:

1. 创建一个Queue.Queue()队列实例,然后向这个队列内灌数据。

2. 将灌进数据的实例传递给线程类,然后通过继承threading.Thread的方式创建。

3. 生成守护进程池(t.setDaemon(True))。

4. 每次从queue中pop一个项目,并使用该线程中的数据和run方法以执行相应的工作。

5. 在完成这项工作之后,使用queue.task_done()方法向任务完成的队列发送一个信号。

6. 对队列执行join操作,实际上意味着等到queue为空,再退出主程序。

在使用这个模式时需要注意一点:通过将守护线程设置为True,将允许主线程或者程序仅在守护线程处于活动状态时才能够退出。这种方式创建了一种简单的方式以控制程序流程,因为在退出之前,你可以对queue执行join操作或者等到队列为空。

说明:

join():保持阻塞状态,直到处理了队列中的所有项目为止。在将一个项目添加到该队列时,未完成的任务的总数就会增加。当使用者线程调用task_done()以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。当未完成的任务的总数减少到零时,join()就会结束阻塞状态。

使用多队列:

因为上面介绍的模式非常有效,所以可以通过连接附加线程池和队列来进行扩展,这是相当简单的。在上面的示例中,您仅仅输出了 Web 页面的开始部分。而下一个示例则将返回各线程获取的完整 Web 页面,然后将结果放置到另一个队列中。然后,对加入到第二个队列中的另一个线程池进行设置,然后对 Web 页面执行相应的处理。这个示例中所进行的工作包括使用一个名为 Beautiful Soup 的第三方 Python 模块来解析 Web 页面。使用这个模块,您只需要两行代码就可以提取所访问的每个页面的 title 标记,并将其打印输出。

代码片段:

#!/usr/bin/env python
#-*- coding:utf-8 -*-

import Queue
import threading
import urllib2
import time
from BeautifulSoup import BeautifulSoup

hosts = [‘http://www.baidu.com‘, ‘http://www.letv.com‘, ‘http://www.sina.com.cn‘, ‘http://www.sohu.com‘, ‘http://www.jd.com‘, ‘http://WWW.51CTO.COM‘, ‘http://www.baidu.com‘, ‘http://www.sina.com.cn‘, ‘http://www.sohu.com‘] 

queue = Queue.Queue()
out_queue = Queue.Queue()

class ThreadUrl(threading.Thread):
    def __init__(self, queue, out_queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.out_queue = out_queue
    def run(self):
        while True:
            host = self.queue.get()
            url = urllib2.urlopen(host)
            chunk = url.read()
            self.out_queue.put(chunk)
            self.queue.task_done()

class DatamineThread(threading.Thread):
    def __init__(self, out_queue):
        threading.Thread.__init__(self)
        self.out_queue = out_queue

    def run(self):
        while True:
            try:
                chunk = self.out_queue.get()
                soup = BeautifulSoup(chunk)
                print soup.findAll([‘title‘])
                self.out_queue.task_done()
            except:
                pass

def main():
    for i in range(5):
        t = ThreadUrl(queue, out_queue)
        t.setDaemon(True)
        t.start()

    for host in hosts:
        queue.put(host)

    for i in range(5):
        dt = DatamineThread(out_queue)
        dt.setDaemon(True)
        dt.start()

    queue.join()
    out_queue.join()

if __name__ == "__main__":
    start = time.time()
    main()
    print "Elapsed Time: %s" % (time.time() - start)

执行结果:

通过该代码您可以看到,我们添加了另一个队列的实例,然后将该队列传递给第一个线程池类ThreadUrl.接下来,对于另一个线程池DatamineThread,几乎复制了完全相同的结构。在这个类的run方法中,从队列中的各个线程获取web页面、文本块,然后使用Beautiful Soup处理这个文本块。在这个实例中,使用Beautiful Soup提取每个页面的title标记、并将其打印输出。可以很容易地将这个实例推广到一些更有价值的应用场景,因为您掌握了基本搜索引擎或者数据挖掘工具的核心内容。一种思想是使用Beautiful Soup从每个页面提取链接,然后按照它们进行导航。

总结:

本文研究了 Python 的线程,并且说明了如何使用队列来降低复杂性和减少细微的错误、并提高代码可读性的最佳实践。尽管这个基本模式比较简单,但可以通过将队列和线程池连接在一起,以便将这个模式用于解决各种各样的问题。

最后,还有很重要的一点需要指出,线程并不能解决所有的问题,对于许多情况,使用进程可能更为合适。特别是,当您仅需要创建许多子进程并对响应进行侦听时,那么标准库子进程模块可能使用起来更加容易。

使用Python进行线程编程

时间: 2024-11-07 15:48:05

使用Python进行线程编程的相关文章

使用 Python 进行线程编程

w 使用 Python 进行线程编程https://www.ibm.com/developerworks/cn/aix/library/au-threadingpython/index.html import threading import datetime class ThreadClass(threading.Thread): def run(self): now = datetime.datetime.now() print "%s says Hello World at time: %

[ Python - 14 ] python进程及线程编程

什么是进程: 简单来讲,进程就是操作系统中运行的程序或任务,进程和程序的区别在于进程是动态的,而程序是静态的.进程是操作系统资源管理的最小单位. 什么是线程: 线程是进程的一个实体,是cpu调度和分派的最小单位,它是比进程更小的能独立运行的基本单位,线程本身不拥有资源,但它可以与同属于一个进程的线程共享进程的资源所拥有的全部资源. python多线程编程与GIL: 为了更有效的利用多核处理,就出现了多线程编程,但是问题是线程间数据的一致性和状态的同步如果得到保证,因此python解析器引入了GI

Python 3 并发编程多进程之队列(推荐使用)

Python 3 并发编程多进程之队列(推荐使用) 进程彼此之间互相隔离,要实现进程间通信(IPC),multiprocessing模块支持两种形式:队列和管道,这两种方式都是使用消息传递的. 可以往队列里放任意类型的数据 创建队列的类(底层就是以管道和锁定的方式实现): 1 Queue([maxsize]):创建共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递. 参数介绍: 1 maxsize是队列中允许最大项数,省略则无大小限制. 方法介绍: 1.主要

Python基础-socket编程

一.网络编程 自从互联网诞生以来,现在基本上所有的程序都是网络程序,很少有单机版的程序了. 计算机网络就是把各个计算机连接到一起,让网络中的计算机可以互相通信.网络编程就是如何在程序中实现两台计算机的通信. 举个例子,当你使用浏览器访问新浪网时,你的计算机就和新浪的某台服务器通过互联网连接起来了,然后,新浪的服务器把网页内容作为数据通过互联网传输到你的电脑上. 由于你的电脑上可能不止浏览器,还有QQ.Skype.Dropbox.邮件客户端等,不同的程序连接的别的计算机也会不同,所以,更确切地说,

Python:线程、进程与协程(7)——线程池

前面转载了一篇分析进程池源码的博文,是一篇分析进程池很全面的文章,点击此处可以阅读.在Python中还有一个线程池的概念,它也有并发处理能力,在一定程度上能提高系统运行效率:不正之处欢迎批评指正. 线程的生命周期可以分为5个状态:创建.就绪.运行.阻塞和终止.自线程创建到终止,线程便不断在运行.创建和销毁这3个状态.一个线程的运行时间可由此可以分为3部分:线程的启动时间.线程体的运行时间和线程的销毁时间.在多线程处理的情景中,如果线程不能被重用,就意味着每次创建都需要经过启动.销毁和运行3个过程

Python 的 Socket 编程教程

这是用来快速学习 Python Socket 套接字编程的指南和教程.Python 的 Socket 编程跟 C 语言很像. Python 官方关于 Socket 的函数请看 http://docs.python.org/library/socket.html 基本上,Socket 是任何一种计算机网络通讯中最基础的内容.例如当你在浏览器地址栏中输入 www.oschina.net 时,你会打开一个套接字,然后连接到 www.oschina.net 并读取响应的页面然后然后显示出来.而其他一些聊

【python】多线程编程

使用多线程编程和一个共享的数据结构如queue,这种程序任务可以用多个功能单一的线程来组织: UserRequestThread:负责读取客户的输入,可能是一个I/O信道.程序可能创建多个线程,每个客户一个,请求会被放入队列中 RequestProcessor:一个负责从队列中获取并处理请求的线程,它为下面那种线程提供输出 ReplyThread:负责把给用户的输出取出来,如果是网络应用程序就把结果发送出去,否则就保存到本地文件系统或数据库中. 一个顺序执行单线程的例子: from time i

python之高性能网络编程并发框架eventlet实例

http://blog.csdn.net/mingzznet/article/details/38388299 前言: 虽然 eventlet 封装成了非常类似标准线程库的形式,但线程和eventlet在实际并发执行流程仍然有明显区别.在没有出现 I/O 阻塞时,除非显式声明,否则当前正在执行的 eventlet 永远不会把 cpu 交给其他的 eventlet,而标准线程则是无论是否出现阻塞,总是由所有线程一起争夺运行资源.所有 eventlet 对 I/O 阻塞无关的大运算量耗时操作基本没有

[记录]Python高并发编程

========== ==多进程== ========== 要让Python程序实现多进程(multiprocessing),我们先了解操作系统的相关知识. Unix/Linux操作系统提供了一个fork()系统调用,它非常特殊.普通的函数调用,调用一次,返回一次,但是fork()调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后,分别在父进程和子进程内返回. 子进程永远返回0,而父进程返回子进程的ID.这样做的理由是,一个父进程可以fork出很多子进程,