如何使用Python实现并发编程
2015-05-25
Python  / 已有2575人围观

    Python作为一种解释型语言,由于使用了全局解释锁(GIL)的原因,其代码不能同时在多核CPU上并发的运行。这也导致在Python中使用多线程编程并不能实现并发,我们得使用其他的方法在Python中实现并发编程。


一、全局解释锁(GIL)


    Python中不能通过使用多线程实现并发编程主要是因为全局解释锁的机制,所以首先解释一下全局解释锁的概念。

    首先,我们知道C++和Java是编译型语言,而Python则是一种解释型语言。对于Python程序来说,它是直接被输入到解释器中直接运行的。解释器在程序执行之前对其并不了解;它所知道的只是Python的规则,以及在执行过程中怎样去动态的应用这些规则。它也有一些优化,但是这基本上只是另一个级别的优化。由于解释器没法很好的对程序进行推导,Python的大部分优化其实是解释器自身的优化。更快的解释器自然意味着程序的运行也能“免费”的更快。也就是说,解释器优化后,Python程序不用做修改就可以享受优化后的好处。

    为了利用多核系统,Python必须支持多线程运行。但作为解释型语言,Python的解释器需要做到既安全又高效。解释器要注意避免在不同的线程操作内部共享的数据,同时还要保证在管理用户线程时保证总是有最大化的计算资源。为了保证不同线程同时访问数据时的安全性,Python使用了全局解释器锁(GIL)的机制。从名字上我们很容易明白,它是一个加在解释器上的全局(从解释器的角度看)锁(从互斥或者类似角度看)。这种方式当然很安全,但它也意味着:对于任何Python程序,不管有多少的处理器,任何时候都总是只有一个线程在执行。即:只有获得了全局解释器锁的线程才能操作Python对象或者调用Python/C API函数。

    所以,在Python中”不要使用多线程,请使用多进程”。具体来说,如果你的代码是IO密集型的,使用多线程或者多进程都是可以的,多进程比线程更易用,但是会消耗更多的内存;如果你的代码是CPU密集型的,多进程(multiprocessing模块)就明显是更好的选择——特别是所使用的机器是多核或多CPU的时候。

    另外,Python的官方实现CPython带有GIL,但并不是所有的Python实现版本都是这样的。IronPython,Jython,还有使用.NET框架实现的Python就没有GIL。所以如果你不能忍受GIL,也可以尝试用一下其他实现版本的Python。


二、线程(Thread)


    多线程是大部分语言实现并发编程的常用方法,使用多线程可以有效的利用CPU资源,但是Python除外,这主要是因为上面提到的坑爹的全局解释锁。甚至有时候你会发现:在Python中使用了多线程之后导致代码的执行效率下降了!

    在Python中使用线程有两种方法:thread模块和threading模块。提供两组线程的接口,一组是thread模块,提供基础的,低等级(Low Level)接口,使用Function作为线程的运行体。还有一组是threading模块,通过对thread模块的进行二次封装,提供更容易使用的基于对象的接口(类似于Java),可以继承Thread对象来实现线程,还提供了其它一些线程相关的对象,例如Timer,Lock

# thread模块
import thread
def worker():
    """ thread worker function """
    print 'Thread Worker'
thread.start_new_thread(worker)

# threading模块1
import threading
def worker():
    """ thread worker function """
    print 'Thread Worker'
t = threading.Thread(target=worker)
t.start()

# threading模块2
import threading
class worker(threading.Thread):
    def __init__(self):
        pass

    def run():
        """ thread worker function """
        print 'Thread Worker'
     
t = worker()
t.start()


三、进程(Process)


    由于前文提到的全局解释锁的问题,使得多进程变成了Python下实现并发编程比较好的方法,多进程也可以比较有效的使用CPU资源,实现并发编程。但是很明显的问题,进程的开销比线程要大,使用多进程实现并发编程需要你的机器配置比较高(根据你的具体需求确定)。

    Python的mutliprocessing模块和threading具有类似的接口。

from multiprocessing import Process
 
def worker():
    """thread worker function"""
    print 'Worker'
p = Process(target=worker)
p.start()
p.join()

    由于线程共享相同的地址空间和内存,所以线程之间的通信是非常容易的,然而进程之间的通信就要复杂一些了。常见的进程间通信有,管道,消息队列,Socket接口(TCP/IP)等等。Python的mutliprocessing模块提供了封装好的管道和队列,可以方便的在进程间传递消息。另外,Python还提供了进程池Pool对象,可以方便的管理和控制线程。


四、concurrent模块


    concurrent.futures是Python 3.2版本引入的模块,用于并发处理,类似于其他语言中的线程池。在Python中,使用mutliprocessing模块可以实现多进程,但是过于繁琐;更好的选择是concurrent.futures。示例代码如下:

In [1]: from concurrent.futures import ThreadPoolExecutor

In [2]: with ThreadPoolExecutor(max_workers=1) as executor:
   ...:     future = executor.submit(pow, 323, 1235)
   ...:     print(future.result())


参考:

    Understanding GIL

    使用Python进行并发编程

    简单实现并发:python concurrent模块

    Python's Hardest Problem (翻译版本)

    Python并发与并行的新手指南


Over!

本文地址:http://xianglong.me/article/how-to-implement-concurrent-with-python/

特别声明:本站文章,如非注明,皆为降龙原创。转载需注明本文链接并保证链接可用。