简介

使用Python可以快速地编写程序,但是python对多线程的支持却不好,在Python2中,更多地使用多进程。在Python3中,引入了concurrent,便于多线程/进程开发。

Python GIL

Python代码的执行由Python解释器进行控制,目前Python的解释器有多种,比较著名的有CPython、PyPy、Jython等。其中CPython为最广泛使用的Python解释器,是最早的由c语言开发。

在OS中,支持多个线程同时执行。 但在Python设计之初考虑到在Python解释器的主循环中执行Python代码,于是CPython中设计了全局解释器锁GIL(Global Interpreter Lock)机制,用于管理解释器的访问,Python线程的执行必须先竞争到GIL权限才能执行。
因此无论是单核还是多核CPU,任意给定时刻只有一个线程会被Python解释器执行,无法多线程运行。并这也是为什么在多核CPU上,Python的多线程有时效率并不高的根本原因。

Python2中高性能解决方法

Python多任务的解决方案主要由这么几种:

  • 启动多进程,每个进程只有一个线程,通过多进程执行多任务;
  • 启动单进程,在进程内启动多线程,通过多线程执行多任务;
  • 启动多进程,在每个进程内再启动多个线程,同时执行更多的任务–这样子太复杂,实际上效果并不好,使用的更少。

使用多进程

多进程的package对应的是multiprocessing。

先看一下Process类。

'''
from multiprocessing.process import Process, current_process, active_children
'''class Process(object):'''Process objects represent activity that is run in a separate processThe class is analagous to `threading.Thread`'''_Popen = Nonedef __init__(self, group=None, target=None, name=None, args=(), kwargs={}):assert group is None, 'group argument must be None for now'count = _current_process._counter.next()self._identity = _current_process._identity + (count,)self._authkey = _current_process._authkeyself._daemonic = _current_process._daemonicself._tempdir = _current_process._tempdirself._parent_pid = os.getpid()self._popen = Noneself._target = targetself._args = tuple(args)self._kwargs = dict(kwargs)self._name = name or type(self).__name__ + '-' + \':'.join(str(i) for i in self._identity)

一个简单的Process的使用示例:

from multiprocessing import Processdef f(name):print 'hello', nameif __name__ == '__main__':p = Process(target=f, args=('bob',))p.start()p.join()

多线程处理

线程处理的package是threading.

先简单看一下Thread类

# Main class for threadsclass Thread(_Verbose):"""A class that represents a thread of control.This class can be safely subclassed in a limited fashion."""__initialized = False# Need to store a reference to sys.exc_info for printing# out exceptions when a thread tries to use a global var. during interp.# shutdown and thus raises an exception about trying to perform some# operation on/with a NoneType__exc_info = _sys.exc_info# Keep sys.exc_clear too to clear the exception just before# allowing .join() to return.__exc_clear = _sys.exc_cleardef __init__(self, group=None, target=None, name=None,args=(), kwargs=None, verbose=None):"""This constructor should always be called with keyword arguments. Arguments are:*group* should be None; reserved for future extension when a ThreadGroupclass is implemented.*target* is the callable object to be invoked by the run()method. Defaults to None, meaning nothing is called.*name* is the thread name. By default, a unique name is constructed ofthe form "Thread-N" where N is a small decimal number.*args* is the argument tuple for the target invocation. Defaults to ().*kwargs* is a dictionary of keyword arguments for the targetinvocation. Defaults to {}.If a subclass overrides the constructor, it must make sure to invokethe base class constructor (Thread.__init__()) before doing anythingelse to the thread."""

简单示例

#!/usr/bin/python
from threading import Threaddef count(n):print "begin count..." "\r\n"while n > 0:n-=1print "done."def test_ThreadCount():t1 = Thread(target=count,args=(1000000,))print("start thread.")t1.start()print "join thread." t1.join()if __name__ == '__main__':    test_ThreadCount()

输出:

start thread.
begin count...
join thread.done.

使用多进程和多线程性能对比

测试代码是网友的,使用了timeit, 请先安装此包。

#!/usr/bin/python
from threading import Thread
from multiprocessing import Process,Manager
from timeit import timeitdef count(n):while n > 0:n-=1def test_normal():count(1000000)count(1000000)def test_Thread():t1 = Thread(target=count,args=(1000000,))t2 = Thread(target=count,args=(1000000,))t1.start()t2.start()t1.join()t2.join()def test_Process():t1 = Process(target=count,args=(1000000,))t2 = Process(target=count,args=(1000000,))t1.start()t2.start()t1.join()t2.join()if __name__ == '__main__':print "test_normal",timeit('test_normal()','from __main__ import test_normal',number=10)print "test_Thread",timeit('test_Thread()','from __main__ import test_Thread',number=10)print "test_Process",timeit('test_Process()','from __main__ import test_Process',number=10)

执行后的输出结果:

test_normal 1.0291161
test_Thread 7.5084157
test_Process 1.6441867

可见,直接使用方法反而最快,使用Process次之,使用Thread最慢。单这个测试只是运算测试。如果有IO类的慢速操作时,还是要使用Process或者Thread。

python3中的concurrent.futures包

使用java或者CSharp的开发者,对future应该比较了解。这个是用以并发支持。
在Python3.2中提供了concurrent.futures包, 而python 2.7需要安装futures模块,使用命令pip install futures安装即可.

模块concurrent.futures给开发者提供一个执行异步调用的高级接口。concurrent.futures基本上就是在Python的threadingmultiprocessing模块之上构建的抽象层,更易于使用。尽管这个抽象层简化了这些模块的使用,但是也降低了很多灵活性。

这里最重要的是类Executor,当然Executor是抽象类,具体的实现类有2个,分别是ThreadPoolExecutorProcessPoolExecutor,正如名字所示,分别对应着Thread和Process的执行池。

看一下ProcessPoolExecutor定义, 缺省地,最大的工作任务应该和CPU数量匹配。


class ProcessPoolExecutor(_base.Executor):def __init__(self, max_workers=None):"""Initializes a new ProcessPoolExecutor instance.Args:max_workers: The maximum number of processes that can be used toexecute the given calls. If None or not given then as manyworker processes will be created as the machine has processors."""_check_system_limits()if max_workers is None:self._max_workers = multiprocessing.cpu_count()else:if max_workers <= 0:raise ValueError("max_workers must be greater than 0")self._max_workers = max_workers

再看一下ThreadPoolExecutor的定义, 最重叠IO上(或者参考CompleteIO),处理最大的工作数量应该cpu数量的5倍。

class ThreadPoolExecutor(_base.Executor):def __init__(self, max_workers=None):"""Initializes a new ThreadPoolExecutor instance.Args:max_workers: The maximum number of threads that can be used toexecute the given calls."""if max_workers is None:# Use this number because ThreadPoolExecutor is often# used to overlap I/O instead of CPU work.max_workers = (cpu_count() or 1) * 5if max_workers <= 0:raise ValueError("max_workers must be greater than 0")self._max_workers = max_workersself._work_queue = queue.Queue()self._threads = set()self._shutdown = Falseself._shutdown_lock = threading.Lock()

看一个简单的示例,改编自网友的程序:

#!/usr/bin/python2
import os
import urllibfrom concurrent.futures import ThreadPoolExecutor
from concurrent.futures import as_completed
from concurrent.futures import ProcessPoolExecutordef downloader(url):req = urllib.urlopen(url)if (req != None):print "begin down", url filename = os.path.basename(url)ext = os.path.splitext(url)[1]if not ext:raise RuntimeError("URL does not contain an extension")with open(filename,"wb") as file_handle:while True:chunk = req.read(1024)if not chunk:breakfile_handle.write(chunk)msg = "Finished downloading {filename}".format(filename = filename)return msgdef mainProcess(urls):with ProcessPoolExecutor(max_workers = 5) as executor:futures = [executor.submit(downloader,url) for url in urls]for future in as_completed(futures):print(future.result())def mainThread(urls):with ThreadPoolExecutor(max_workers = 5) as executor:futures = [executor.submit(downloader,url) for url in urls]for future in as_completed(futures):print(future.result())if __name__ == "__main__":urls1 = ["http://www.irs.gov/pub/irs-pdf/f1040.pdf","http://www.irs.gov/pub/irs-pdf/f1040a.pdf","http://www.irs.gov/pub/irs-pdf/f1040ez.pdf"]urls2 = ["http://www.irs.gov/pub/irs-pdf/f1040es.pdf","http://www.irs.gov/pub/irs-pdf/f1040sb.pdf"]mainProcess(urls1)mainThread(urls2)

执行3次,输出如下:

----1
begin down http://www.irs.gov/pub/irs-pdf/f1040ez.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040a.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040.pdf
Finished downloading f1040ez.pdf
Finished downloading f1040.pdf
Finished downloading f1040a.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040es.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040sb.pdf
Finished downloading f1040sb.pdf
Finished downloading f1040es.pdf----2
begin down http://www.irs.gov/pub/irs-pdf/f1040.pdfb
egin down http://www.irs.gov/pub/irs-pdf/f1040ez.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040a.pdf
Finished downloading f1040ez.pdf
Finished downloading f1040a.pdf
Finished downloading f1040.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040es.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040sb.pdf
Finished downloading f1040sb.pdf
Finished downloading f1040es.pdf----3
begin down http://www.irs.gov/pub/irs-pdf/f1040.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040a.pdf
Finished downloading f1040.pdf
Finished downloading f1040a.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040ez.pdf
Finished downloading f1040ez.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040sb.pdf
begin down http://www.irs.gov/pub/irs-pdf/f1040es.pdf
Finished downloading f1040sb.pdf
Finished downloading f1040es.pdf

python中的多线程、多进程相关推荐

  1. js打印线程id_浅谈python中的多线程和多进程(二)

    原创:hxj7 本文继续分享一个关于python多线程和多进程区别的例子 前文<浅谈python中的多线程和多进程>中我们分享过一个例子,就是分别利用python中的多线程和多进程来解决高 ...

  2. 获得进程id_浅谈python中的多线程和多进程(二)

    原创:hxj7 本文继续分享一个关于python多线程和多进程区别的例子 前文<浅谈python中的多线程和多进程>中我们分享过一个例子,就是分别利用python中的多线程和多进程来解决高 ...

  3. python中的多线程求值串行和并行_python多线程和多进程——python并行编程实验

    工作中经常涉及到加速程序的运行,除了代码逻辑的优化,算法的优化之外,还经常使用的一招就是并发编程.至于python的并型编程这一块.说到并行编程,我们不得不谈线程和进程这两个概念: + 进程:对于操作 ...

  4. Python中的多线程

    Python中的多线程 文章目录 Python中的多线程 一.线程介绍 1.什么是线程 2.为什么要使用多线程 3.多线程的优点 二.线程实现 1.**`普通创建方式`** 2.**`自定义线程`** ...

  5. 浅谈 Python 中的多线程。

    本文作者:Rocky0249 公众号:Python空间 00.写在之前 大家好,我是 Rocky0429,今天我来写一下 Python 中的多线程.在正式开始之前,我先用比较通俗的语言给大家介绍几个比 ...

  6. Python中的多线程是假的多线程?

    Python中的多线程是假的多线程? 为什么这么说,我们先明确一个概念,全局解释器锁(GIL)global interpreter lock Python代码的执行由Python虚拟机(解释器)来控制 ...

  7. PYTHON 中的多线程

    通常我们构建的应用程序可能需要多个任务在同一个应用程序中同时运行.这就是python中多线程概念发挥作用的地方.这篇文章提供了在 Python 中使用多线程(又名 Python 中的线程)的全面解释. ...

  8. 万字长文!终于讲透了Python中的多线程和多进程!

    我们知道,在一台计算机中,我们可以同时打开许多软件,比如同时浏览网页.听音乐.打字等等,看似非常正常.但仔细想想,为什么计算机可以做到这么多软件同时运行呢?这就涉及到计算机中的两个重要概念:多进程和多 ...

  9. Python中的多线程是假的多线程

    Python多线程相当于单核多线程,多线程有两个好处:CPU并行,IO并行,单核多线程相当于自断一臂.所以,在Python中,可以使用多线程,但不要指望能有效利用多核.如果一定要通过多线程利用多核,那 ...

  10. 一篇文章学习Python中的多线程

    基本知识: 一.什么是多线程Threading 二.添加线程Thread 三.join功能 四.存储进程结果Queue 五.GIL不一定有效率 六.线程锁 Lock 一.什么是多线程Threading ...

最新文章

  1. webform(七)分页
  2. 稳~阿里程序员常用的 15 款开发者工具
  3. java解析xml实例_在java中使用dom解析xml的示例分析
  4. 人工智能们再也不用担心撞上玻璃橱窗了
  5. struts2校验再提交多条提示信息
  6. 大数据开发者必走的学习路线
  7. android设置存储,在android应用程序中存储用户设置的最合适方法是什么?
  8. php时间戳对比,php+js+时间戳比较,输出不同内容
  9. java实时读取文件内容,java实时读取和写入文件
  10. 别再瞎学 Python 了!
  11. K8S 使用 SideCar 模式部署 Filebeat 收集容器日志
  12. js中数组原型Array、自定义原型函数Array.prototype
  13. elasticsearch sql插件 2.4及以下版本配置
  14. mergsort.c
  15. 计算机技术概论知识点,《计算机基础概论》知识点.pdf
  16. mac电脑如何设置开机启动项
  17. 用户注册的邮箱激活模块的设计与实现
  18. linux课程设计tiny6410,tiny6410烧写linux
  19. 让你的 Xcode8 继续使用插件
  20. 文本超出省略号、css三角形、画虚线边框、修改输入框placeholder样式、修改滚动条样式、css实现优惠券

热门文章

  1. 尚洋优选健康美电商平台启动仪式在广州召开
  2. mysql5.623 GTID主从复制+半同步复制安装与配置
  3. WPF 和 windows Form Application的区别
  4. 使用js命名空间进行模块式开发
  5. django在nginx uwsgi和tornado异步方案在项目中的体验
  6. 厚积薄发,丰富的公用类库积累,助你高效进行系统开发(11)---各种线程相关操作类...
  7. (转)NSIS使用心得
  8. AspectJ 中的pointcut 语法
  9. 日语python怎么说_python+Mecab,一次性学会日语分词
  10. influxdb 客户端工具_性能工具之Jmeter小白入门系列之六