我试图理解多处理相对于线程的优势。我知道多处理绕过了全局解释器锁,但是还有什么其他的优势,线程不能做同样的事情吗?


当前回答

线程模块使用线程,多处理模块使用进程。不同之处在于线程在相同的内存空间中运行,而进程有单独的内存。这使得在多进程之间共享对象变得有点困难。由于线程使用相同的内存,必须采取预防措施,否则两个线程将同时写入同一内存。这就是全局解释器锁的作用。

生成进程比生成线程要慢一些。

其他回答

关键的优势是隔离。进程崩溃不会导致其他进程崩溃,而线程崩溃可能会对其他线程造成严重破坏。

线程模块使用线程,多处理模块使用进程。不同之处在于线程在相同的内存空间中运行,而进程有单独的内存。这使得在多进程之间共享对象变得有点困难。由于线程使用相同的内存,必须采取预防措施,否则两个线程将同时写入同一内存。这就是全局解释器锁的作用。

生成进程比生成线程要慢一些。

以下是我想到的一些优点和缺点。

多处理

Pros

独立的内存空间 代码通常很简单 利用多个cpu和核 避免了cPython的GIL限制 消除了对同步原语的大部分需求,除非您使用共享内存(相反,它更像是IPC的通信模型) 子进程是可中断/可杀死的 Python多处理模块包含有用的抽象,其接口类似于线程。线程 必须使用cPython进行cpu绑定处理

Cons

IPC有点复杂,开销更大(通信模型vs.共享内存/对象) 更大的内存占用

线程

Pros

轻量级——低内存占用 共享内存-使访问状态从另一个上下文更容易 允许您轻松地创建响应式ui 正确释放GIL的cPython C扩展模块将并行运行 对于I/ o约束应用程序来说是一个很好的选择

Cons

cPython -服从GIL 不是可中断/ killable 如果不遵循命令队列/消息泵模型(使用queue模块),则必须手动使用同步原语(需要对锁定的粒度进行决策) 代码通常更难理解和正确编写——竞争条件的可能性急剧增加

多处理

Multiprocessing通过增加cpu来提高计算能力。 多个进程同时执行。 创建流程既耗时又耗费资源。 多处理可以是对称的也可以是非对称的。

Python中的多处理库使用独立的内存空间,多个CPU核心,绕过CPython中的GIL限制,子进程是可杀死的(例如程序中的函数调用),并且更容易使用。 该模块的一些注意事项是内存占用较大,IPC稍微复杂一些,开销更大。

多线程

多线程创建单个进程的多个线程,以提高计算能力。 一个进程的多个线程并发执行。 线程的创建在时间和资源上都是经济的。

多线程库是轻量级的,共享内存,负责响应式UI,并用于I/O绑定应用程序。 该模块不可杀死,并受GIL约束。 多个线程生活在同一个进程中的同一个空间中,每个线程将执行特定的任务,有自己的代码,自己的堆栈内存,指令指针,并共享堆内存。 如果一个线程有内存泄漏,它会损害其他线程和父进程。

使用Python的多线程和多处理示例

Python 3有启动并行任务的功能。这使我们的工作更容易。

它有线程池和进程池。

下面让我们来了解一下:

ThreadPoolExecutor例子

import concurrent.futures
import urllib.request

URLS = ['http://www.foxnews.com/',
        'http://www.cnn.com/',
        'http://europe.wsj.com/',
        'http://www.bbc.co.uk/',
        'http://some-made-up-domain.com/']

# Retrieve a single page and report the URL and contents
def load_url(url, timeout):
    with urllib.request.urlopen(url, timeout=timeout) as conn:
        return conn.read()

# We can use a with statement to ensure threads are cleaned up promptly
with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    # Start the load operations and mark each future with its URL
    future_to_url = {executor.submit(load_url, url, 60): url for url in URLS}
    for future in concurrent.futures.as_completed(future_to_url):
        url = future_to_url[future]
        try:
            data = future.result()
        except Exception as exc:
            print('%r generated an exception: %s' % (url, exc))
        else:
            print('%r page is %d bytes' % (url, len(data)))

ProcessPoolExecutor

import concurrent.futures
import math

PRIMES = [
    112272535095293,
    112582705942171,
    112272535095293,
    115280095190773,
    115797848077099,
    1099726899285419]

def is_prime(n):
    if n % 2 == 0:
        return False

    sqrt_n = int(math.floor(math.sqrt(n)))
    for i in range(3, sqrt_n + 1, 2):
        if n % i == 0:
            return False
    return True

def main():
    with concurrent.futures.ProcessPoolExecutor() as executor:
        for number, prime in zip(PRIMES, executor.map(is_prime, PRIMES)):
            print('%d is prime: %s' % (number, prime))

if __name__ == '__main__':
    main()

另一件没有提到的事情是,它取决于你使用的是什么操作系统。在Windows中,进程是昂贵的,所以线程在Windows中会更好,但在unix中,进程比它们的Windows变体更快,所以在unix中使用进程要安全得多,而且生成速度快。