zoukankan      html  css  js  c++  java
  • python 多线程剖析

    先来看个栗子:

    下面来看一下I/O秘籍型的线程,举个栗子——爬虫,下面是爬下来的图片用4个线程去写文件

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3  
     4 import re
     5 import urllib
     6 import threading
     7 import Queue
     8 import timeit
     9 
    10 def getHtml(url):
    11     html_page = urllib.urlopen(url).read()
    12     return html_page
    13  
    14 # 提取网页中图片的URL
    15 def getUrl(html):
    16     pattern = r'src="(http://img.*?)"'  # 正则表达式
    17     imgre = re.compile(pattern)
    18     imglist = re.findall(imgre, html)  # re.findall(pattern,string)  在string中寻找所有匹配成功的字符串,以列表形式返回值
    19     return imglist
    20  
    21 class getImg(threading.Thread):
    22     def __init__(self, queue, thread_name=0):  # 线程公用一个队列
    23         threading.Thread.__init__(self)
    24         self.queue = queue
    25         self.thread_name = thread_name
    26         self.start()  # 启动线程
    27  
    28     # 使用队列实现进程间通信
    29     def run(self):
    30         global count
    31         while (True):
    32             imgurl = self.queue.get() # 调用队列对象的get()方法从队头删除并返回一个项目
    33             urllib.urlretrieve(imgurl, 'E:mntgirls\%s.jpg' % count)
    34             count += 1
    35             if self.queue.empty():
    36                 break
    37             self.queue.task_done()  # 当使用者线程调用 task_done() 以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。
    38 imglist = []
    39 def main():
    40     global imglist
    41     url = "http://huaban.com/favorite/beauty/"  # 要爬的网页地址
    42     html = getHtml(url)
    43     imglist = getUrl(html)
    44  
    45 def main_1():
    46     global count
    47     threads = []
    48     count = 0
    49     queue = Queue.Queue()
    50     # 将所有任务加入队列
    51     for img in imglist:
    52         queue.put(img)
    53     # 多线程爬去图片
    54     for i in range(4):
    55         thread = getImg(queue, i)
    56         threads.append(thread)
    57     # 阻塞线程,直到线程执行完成
    58     for thread in threads:
    59         thread.join()
    60  
    61 if __name__ == '__main__':
    62     main()
    63     t = timeit.Timer(main_1)
    64     print t.timeit(1)

    4个线程的执行耗时为:0.421320716723秒

    修改一下main_1换成单线程的:

     1 def main_1():
     2     global count
     3     threads = []
     4     count = 0
     5     queue = Queue.Queue()
     6     # 将所有任务加入队列
     7     for img in imglist:
     8         queue.put(img)
     9     # 多线程爬去图片
    10     for i in range(1):
    11         thread = getImg(queue, i)
    12         threads.append(thread)
    13     # 阻塞线程,直到线程执行完成
    14     for thread in threads:
    15         thread.join()

    单线程的执行耗时为:1.35626623274秒

    再来看一个:

     1 #!/usr/bin/env python
     2 # -*- coding:utf-8 -*-
     3 import threading
     4 import timeit
     5 
     6 def countdown(n):
     7     while n > 0:
     8         n -= 1
     9 
    10 def task1():
    11     COUNT = 100000000
    12     thread1 = threading.Thread(target=countdown, args=(COUNT,))
    13     thread1.start()
    14     thread1.join()
    15 
    16 def task2():
    17     COUNT = 100000000
    18     thread1 = threading.Thread(target=countdown, args=(COUNT // 2,))
    19     thread2 = threading.Thread(target=countdown, args=(COUNT // 2,))
    20     thread1.start()
    21     thread2.start()
    22     thread1.join()
    23     thread2.join()
    24 
    25 if __name__ == '__main__':
    26     t1 = timeit.Timer(task1)
    27     print "countdown in one thread ", t1.timeit(1)
    28     t2 = timeit.Timer(task2)
    29     print "countdown in two thread ", t2.timeit(1)

    task1是单线程,task2是双线程,在我的4核的机器上的执行结果:

    countdown in one thread  3.59939150155

    countdown in two thread  9.87704289712

    天呐,双线程比单线程计算慢了2倍多,这是为什么呢,因为countdown是CPU密集型任务(计算嘛)

      I/O密集型任务:线程做I/O处理的时候会释放GIL,其他线程获得GIL,当该线程再做I/O操作时,又会释放GIL,如此往复;

      CPU密集型任务:在多核多线程比单核多线程更差,原因是单核多线程,每次释放GIL,唤醒的哪个线程都能获取到GIL锁,所以能够无缝执行(单核多线程的本质就是顺序执行),但多核,CPU0释放GIL后,其他CPU上的线程都会进行竞争,但GIL可能会马上又被CPU0(CPU0上可能不止一个线程)拿到,导致其他几个CPU上被唤醒后的线程会醒着等待到切换时间后又进入待调度状态,这样会造成线程颠簸(thrashing),导致效率更低。


    作者:Andy
    出处:http://www.cnblogs.com/onepiece-andy/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 相关阅读:
    OpenStack功能简介
    Openstack(七)keystone
    Openstack(六)RabbitMQ集群
    Openstack(五)Memcache- repcached集群
    Openstack(四)Mysql主从
    Openstack(三)Haproxy+Keepalived双机
    Openstack(二)基本环境准备--网络、时间、yum源等
    Openstack架构简介(一)
    运维监控系统之Open-Falcon
    python高级之Flask框架
  • 原文地址:https://www.cnblogs.com/onepiece-andy/p/python-thread-analyze.html
Copyright © 2011-2022 走看看