Python Multithreading 例子

来源:互联网 发布:dhcp端口号是多少 编辑:程序博客网 时间:2024/06/16 00:43

使用线程队列Queue[引用--http://www.ibm.com/developerworks/cn/aix/library/au-threadingpython/index.html]

如前所述,当多个线程需要共享数据或者资源的时候,可能会使得线程的使用变得复杂。线程模块提供了许多同步原语,包括信号量、条件变量、事件和锁。当这些选项存在时,最佳实践是转而关注于使用队列。相比较而言,队列更容易处理,并且可以使得线程编程更加安全,因为它们能够有效地传送单个线程对资源的所有访问,并支持更加清晰的、可读性更强的设计模式。

在下一个示例中,您将首先创建一个以串行方式或者依次执行的程序,获取网站的 URL,并显示页面的前 1024 个字节。有时使用线程可以更快地完成任务,下面就是一个典型的示例。首先,让我们使用urllib2 模块以获取这些页面(一次获取一个页面),并且对代码的运行时间进行计时: 

URL获取序列

                        import urllib2        import time                hosts = ["http://yahoo.com", "http://google.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 = urllib2.urlopen(host)          print url.read(1024)                print "Elapsed Time: %s" % (time.time() - start)      

在运行以上示例时,您将在标准输出中获得大量的输出结果。但最后您将得到以下内容:

        Elapsed Time: 2.40353488922          

让我们仔细分析这段代码。您仅导入了两个模块。首先,urllib2 模块减少了工作的复杂程度,并且获取了 Web 页面。然后,通过调用time.time(),您创建了一个开始时间值,然后再次调用该函数,并且减去开始值以确定执行该程序花费了多长时间。最后分析一下该程序的执行速度,虽然“2.5 秒”这个结果并不算太糟,但如果您需要检索数百个 Web 页面,那么按照这个平均值,就需要花费大约 50 秒的时间。研究如何创建一种可以提高执行速度的线程化版本:

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

hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
        "http://ibm.com", "http://apple.com"]

queue = Queue.Queue()

class ThreadUrl(threading.Thread):
    """Threaded Url Grab"""
    def __init__(self, queue):
        threading.Thread.__init__(self)
        self.queue = queue

    def run(self):
        while True:
            #grabs host from queue
            host = self.queue.get()

            #grabs urls of hosts and prints first 1024 bytes of page
            url = urllib2.urlopen(host)
            print url.read(1024)

            #signals to queue job is done
            self.queue.task_done()

start = time.time()
def main():
    
    #spawn a pool of threads, and pass them queue instance
    for i in range(5):
        t = ThreadUrl(queue)
        t.setDaemon(True)
        t.start()

    #populate queue with data
    for host in hosts:
        queue.put(host)
    
    #wait on the queue until everything has been processed
    queue.join()
main()
print "Elapsed Time: %s" % (time.time() - start)

对于这个示例,有更多的代码需要说明,但与第一个线程示例相比,它并没有复杂多少,这正是因为使用了队列模块。在 Python 中使用线程时,这个模式是一种很常见的并且推荐使用的方式。具体工作步骤描述如下:

  1. 创建一个 Queue.Queue() 的实例,然后使用数据对它进行填充。
  2. 将经过填充数据的实例传递给线程类,后者是通过继承 threading.Thread 的方式创建的。
  3. 生成守护线程池。
  4. 每次从队列中取出一个项目,并使用该线程中的数据和 run 方法以执行相应的工作。
  5. 在完成这项工作之后,使用 queue.task_done() 函数向任务已经完成的队列发送一个信号。
  6. 对队列执行 join 操作,实际上意味着等到队列为空,再退出主程序。

在使用这个模式时需要注意一点:通过将守护线程设置为 true,将允许主线程或者程序仅在守护线程处于活动状态时才能够退出。这种方式创建了一种简单的方式以控制程序流程,因为在退出之前,您可以对队列执行 join 操作、或者等到队列为空。队列模块文档详细说明了实际的处理过程,请参见参考资料:

join()

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


#!/usr/bin/env python

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

hosts = ["http://yahoo.com", "http://google.com", "http://amazon.com",
        "http://ibm.com", "http://apple.com"]

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

class ThreadUrl(threading.Thread):
    """Threaded Url Grab"""
    def __init__(self, queue, out_queue):
        threading.Thread.__init__(self)
        self.queue = queue
        self.out_queue = out_queue

    def run(self):
        while True:
            #grabs host from queue
            host = self.queue.get()

            #grabs urls of hosts and then grabs chunk of webpage
            url = urllib2.urlopen(host)
            chunk = url.read()

            #place chunk into out queue
            self.out_queue.put(chunk)

            #signals to queue job is done
            self.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(5):
        t = ThreadUrl(queue, out_queue)
        t.setDaemon(True)
        t.start()

    #populate queue with data
    for host in hosts:
        queue.put(host)

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


    #wait on the queue until everything has been processed
    queue.join()
    out_queue.join()

main()

print "Elapsed Time: %s" % (time.time() - start)


多线程DNS Query

import Queue
import threading
import time
import os
import sys
import commands

queue = Queue.Queue()
global log_name
class ThreadDig(threading.Thread):
        """Threaded dig"""
        def __init__(self, queue, log_name):
                threading.Thread.__init__(self)
                self.queue = queue
                self.log_name = log_name

        def run(self):
                while True:
                        #grabs host from queue
                        host = self.queue.get()

                        #grabs urls of hosts and prints first 1024 bytes of page
                        #command = 'hping3 -S -c 3 -p 80 ' + host
                        #status,output = commands.getstatusoutput(command)
                        #index = output.find('ms')
                        #if(index != -1):
                        #       info = output[:index+2]
                        #       os.system('echo ' + info + ' >>' + self.log_name)
                        #else:
                        #       os.system('echo *****NA**********  >>' + self.log_name)
                        os.system('dig +nocmd +nocomment +nostat ' + host + '>> ' + self.log_name)
                        print host
                          #signals to queue job is done
                        self.queue.task_done()

def test():
        print "usage: pdnsdig.py <source_tuple_dir> <dest_log_dir> "
        sourcetupledir = sys.argv[1]
        destlogdir = sys.argv[2]
        print "source_tuple_dir", sourcetupledir, "dest_log_dir", destlogdir
        tuple_pre = "unique_tuple"
        log_pre= "dnsdiglog"
        i = 1
        os.system('pwd')
        global dic
        while True:
                tuple_name = sourcetupledir + tuple_pre + str (i) + '.txt'
                log_name = log_pre + str(i)
                print "Does "+ tuple_name +" exist?"

                if os.path.isfile(tuple_name):
                        print tuple_name+" exists"
                        size = os.path.getsize(tuple_name)
                        time.sleep(5) #secs
                        print 'file size not chaned '
                        if size == os.path.getsize(tuple_name): # file size not changed
                                os.system("echo ===================== >> " + log_name)
                                for record in open(tuple_name):
                                        item=record.split(',')
                                        if(len(item) == 6 and item[2] != ''):
                                                queue.put(item[2])
                                        if(queue.qsize() == 10):
                                                for j in range(10):
                                                        t = ThreadDig(queue, log_name)
                                                        t.setDaemon(True)
                                                        t.start()
                                                queue.join()
                                                print queue.qsize()

                                i = i + 1
                else:
                        time.sleep(5)#sleep 5 secs
if __name__ == '__main__':

               test()