前言

Python作为一种解释性语言,它的速度并不慢。如果对计算速度有很高的要求的话,可以考虑的方法有:(1)使用更快的语言比如C/C++来实现然后Python调用; (2)Python的解释器就是用C语言写的,即CPython。解释器将Python转换成一种中间语言,叫做Python字节码,类似于汇编语言,但是包含一些更高级的指令。当一个运行一个Python程序的时候,评估循环不断将Python字节码转换成机器码。使用即时编译器来替换CPython例如PyPy可以提高运行速度。(3)利用Python提供的很多并行的模块。

进程是应用程序的一个执行实例,比如,在桌面上双击浏览器图标将会运行一个浏览器。线程是一个控制流程,可以在进程内与其他活跃的线程同时执行。“控制流程”指的是顺序执行一些机器指令。进程可以包含多个线程,所以开启一个浏览器,操作系统将创建一个进程,并开始执行这个进程的主线程。每一个线程将独立执行一系列的指令(通常就是一个函数),并且和其他线程并行执行。然而,同一个进程内的线程可以共享一些地址空间和数据结构。

进程

# called_Process.py
print("Hello Python Parallel Cookbook!!")        
closeInput = input("Press ENTER to exit")
print("Closing calledProcess")  
# calling_Process.py
import os
import sys

program = "python"
print("Process calling")
arguments = ["called_Process.py"]

os.execvp(program, (program,) + tuple(arguments))
print("Good Bye!")

运行python calling_Process.py,这里os.execvp开启了一个新的进程,替换了当前的进程,注意”Good Bye!”永远不会打印出来。

线程

threading模块Python1.5.2开始被引入,作为thread模块的增强版本。threading模块可以帮助我们更方便的使用线程工作,运行程序同时执行多个操作。

由于Python有Global Interpreter Lock(GIL)的存在,Python将所有线程在主线程内部运行。所以,当你想去运行多个CPU密集的操作的时候,你会发现它比仅使用单线程时候运行更慢了!所以在Python中使用thread去做它更适合>的事情,比如I/O操作。如果需要更短的运行时间,考虑使用multiprocessing模块。

import threading

def doubler(nbr):
    print(threading.currentThread().getName() + '\n')
    print(nbr * 2)
    print()

if __name__ == "__main__":
    for i in range(5):
        my_thr = threading.Thread(target=doubler, args=(i,))
        my_thr.start()

创建一个线程:

# HelloThread.py
# To use threads you need import Thread using the following code:
from threading import Thread
# Also we use the sleep function to make the thread "sleep"
from time import sleep

# To create a thread in Python you'll want to make your class work as a thread.
# For this, you should subclass your class from the Thread class
class CookBook(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.message = "Hello Parallel Python CookBook!!\n"

    # this method prints only the message
    def print_message(self):
        print(self.message)

    # The run method prints ten times the message
    def run(self):
        print("Thread Starting\n")
        x = 0
        while (x < 10):
            self.print_message()
            sleep(2)
            x += 1
        print("Thread Ended\n")

# start the main process
print("Process Started")

# create an instance of the HelloWorld class
hello_Python = CookBook()

# print the message...starting the thread
hello_Python.start()

# end the main process
print("Process Ended")

执行python HelloThread会每两秒打印一条消息。主程序执行结束的时候,线程依然会每个两秒钟就打印一次信息。此例子证实了线程是在父进程下执行的一个子任务。

需要注意的一点是,永远不要留下任何线程在后台默默运行。否则在大型程序中这将给你带来无限痛苦。

守护线程

守护线程会在不打断主线程的情况下在背后运行。对于非守护线程,它们会插入到主线程的运行中间,我们需要追踪它们并且告诉它们该何时推出了。对于守护线程,我们完全让他们自己去运行,当主程序退出的时候,所有的守护线程都会被自动杀死。

import threading
import time
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='(%(threadName)-9s) %(message)s',)

def n():
    logging.debug('Starting')
    time.sleep(1)
    logging.debug('Exiting')

def d():
    logging.debug('Starting')
    time.sleep(5) # comment this to print out exit information of d
    logging.debug('Exiting')

if __name__ == '__main__':

	t = threading.Thread(name='non-daemon', target=n)

	d = threading.Thread(name='daemon', target=d)
	d.setDaemon(True)

	d.start()
	t.start()

上面的程序输出:

(daemon   ) Starting
(non-daemon) Starting
(non-daemon) Exiting

可以看到并没有守护线程退出的消息,这结果的原因是由于主程序在1秒即退出了,而守护线程需要5秒才能得到结果,在主程序推出时候守护线程提前被杀死因此没有输出。如果将守护线程的时间缩短为1秒内,便可以看到:

(daemon   ) Starting
(non-daemon) Starting
(daemon   ) Exiting
(non-daemon) Exiting

为了等待到一个守护线程去完成它的工作,可以使用t.join()。例如:

import threading
import time
import logging

logging.basicConfig(level=logging.DEBUG,
                    format='(%(threadName)-9s) %(message)s',)

def n():
    logging.debug('Starting')
    time.sleep(1)
    logging.debug('Exiting')

def d():
    logging.debug('Starting')
    time.sleep(5)
    logging.debug('Exiting')

if __name__ == '__main__':

    t = threading.Thread(name='non-daemon', target=n)

    d = threading.Thread(name='daemon', target=d)
    d.setDaemon(True)

    d.start()
    t.start()
    d.join()
    t.join()

可以看到先等到主程序等到守护线程退出才继续退出。虽然t线程后开始,但是由于它运行时间短,所以我们先得到它退出的消息。

这里也可以理解为所有的非守护进程是自动进行join的。

(daemon   ) Starting
(non-daemon) Starting
(non-daemon) Exiting
(daemon   ) Exiting

参考链接