欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python多线程原理与用法实例剖析

程序员文章站 2023-12-04 08:45:52
本文实例讲述了python多线程原理与用法。分享给大家供大家参考,具体如下: 先来看个栗子: 下面来看一下i/o秘籍型的线程,举个栗子——爬虫,下面是爬下来的图片用4个...

本文实例讲述了python多线程原理与用法。分享给大家供大家参考,具体如下:

先来看个栗子:

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

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import re
import urllib
import threading
import queue
import timeit
def gethtml(url):
  html_page = urllib.urlopen(url).read()
  return html_page
# 提取网页中图片的url
def geturl(html):
  pattern = r'src="(http://img.*?)"' # 正则表达式
  imgre = re.compile(pattern)
  imglist = re.findall(imgre, html) # re.findall(pattern,string) 在string中寻找所有匹配成功的字符串,以列表形式返回值
  return imglist
class getimg(threading.thread):
  def __init__(self, queue, thread_name=0): # 线程公用一个队列
    threading.thread.__init__(self)
    self.queue = queue
    self.thread_name = thread_name
    self.start() # 启动线程
  # 使用队列实现进程间通信
  def run(self):
    global count
    while (true):
      imgurl = self.queue.get() # 调用队列对象的get()方法从队头删除并返回一个项目
      urllib.urlretrieve(imgurl, 'e:\mnt\girls\%s.jpg' % count)
      count += 1
      if self.queue.empty():
        break
      self.queue.task_done() # 当使用者线程调用 task_done() 以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。
imglist = []
def main():
  global imglist
  url = "http://huaban.com/favorite/beauty/" # 要爬的网页地址
  html = gethtml(url)
  imglist = geturl(html)
def main_1():
  global count
  threads = []
  count = 0
  queue = queue.queue()
  # 将所有任务加入队列
  for img in imglist:
    queue.put(img)
  # 多线程爬去图片
  for i in range(4):
    thread = getimg(queue, i)
    threads.append(thread)
  # 阻塞线程,直到线程执行完成
  for thread in threads:
    thread.join()
if __name__ == '__main__':
  main()
  t = timeit.timer(main_1)
  print t.timeit(1)

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

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

def main_1():
  global count
  threads = []
  count = 0
  queue = queue.queue()
  # 将所有任务加入队列
  for img in imglist:
    queue.put(img)
  # 多线程爬去图片
  for i in range(1):
    thread = getimg(queue, i)
    threads.append(thread)
  # 阻塞线程,直到线程执行完成
  for thread in threads:
    thread.join()

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

Python多线程原理与用法实例剖析

再来看一个:

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import threading
import timeit
def countdown(n):
  while n > 0:
    n -= 1
def task1():
  count = 100000000
  thread1 = threading.thread(target=countdown, args=(count,))
  thread1.start()
  thread1.join()
def task2():
  count = 100000000
  thread1 = threading.thread(target=countdown, args=(count // 2,))
  thread2 = threading.thread(target=countdown, args=(count // 2,))
  thread1.start()
  thread2.start()
  thread1.join()
  thread2.join()
if __name__ == '__main__':
  t1 = timeit.timer(task1)
  print "countdown in one thread ", t1.timeit(1)
  t2 = timeit.timer(task2)
  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密集型任务(计算嘛)

Python多线程原理与用法实例剖析

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

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

更多关于python相关内容感兴趣的读者可查看本站专题:《python进程与线程操作技巧总结》、《python数据结构与算法教程》、《python函数使用技巧总结》、《python字符串操作技巧汇总》、《python入门与进阶经典教程》、《python+mysql数据库程序设计入门教程》及《python常见数据库操作技巧汇总

希望本文所述对大家python程序设计有所帮助。