多处理与线程化Python
我正在努力理解“超过”的优点。我知道,多处理绕过了全局解释器锁,但是还有什么其他的优势,并且线程不能做同样的事情呢?关键的优势是隔离。进程崩溃不会导致其他进程崩溃,而线程崩溃可能会对其他线程造成严重破坏。线程化模块使用线程,多处理与线程化Python,python,multithreading,multiprocessing,Python,Multithreading,Multiprocessing,我正在努力理解“超过”的优点。我知道,多处理绕过了全局解释器锁,但是还有什么其他的优势,并且线程不能做同样的事情呢?关键的优势是隔离。进程崩溃不会导致其他进程崩溃,而线程崩溃可能会对其他线程造成严重破坏。线程化模块使用线程,多处理模块使用进程。不同之处在于线程在相同的内存空间中运行,而进程有单独的内存。这使得使用多处理的进程之间共享对象变得有点困难。由于线程使用相同的内存,因此必须采取预防措施,否则两个线程将同时写入同一内存。这就是全局解释器锁的作用 生成进程比生成线程慢一点。另一件没有提到的事
多处理模块使用进程。不同之处在于线程在相同的内存空间中运行,而进程有单独的内存。这使得使用多处理的进程之间共享对象变得有点困难。由于线程使用相同的内存,因此必须采取预防措施,否则两个线程将同时写入同一内存。这就是全局解释器锁的作用
生成进程比生成线程慢一点。另一件没有提到的事情是,它取决于您使用的操作系统的速度。在Windows中,进程的成本很高,因此线程在Windows中会更好,但在unix中,进程比其Windows变体更快,因此在unix中使用进程更安全,而且生成速度更快。线程的任务是使应用程序能够响应。假设您有一个数据库连接,并且需要响应用户输入。如果没有线程,如果数据库连接繁忙,应用程序将无法响应用户。通过将数据库连接拆分为单独的线程,可以使应用程序更具响应性。另外,由于两个线程处于同一进程中,因此它们可以访问相同的数据结构—良好的性能,加上灵活的软件设计
请注意,由于GIL,应用程序实际上并不是同时做两件事,但我们所做的是将数据库上的资源锁放在一个单独的线程中,以便CPU时间可以在它和用户交互之间切换。CPU时间在线程之间分配
多处理是指在给定的时间内,您确实希望完成多件事情的情况。假设您的应用程序需要连接到6个数据库,并对每个数据集执行复杂的矩阵转换。将每个作业放在一个单独的线程中可能会有所帮助,因为当一个连接空闲时,另一个连接可能会获得一些CPU时间,但处理不会并行进行,因为GIL意味着您只能使用一个CPU的资源。通过将每个作业放在一个多处理进程中,每个作业都可以在自己的CPU上运行并以最高效率运行。以下是我提出的一些优点/缺点
多处理
赞成的意见
- 独立存储空间
- 代码通常很简单
- 利用多个CPU和内核
- 避免cPython的GIL限制
- 消除了对同步原语的大部分需求,除非您使用共享内存(相反,它更像是IPC的通信模型)
- 子进程是可中断/可终止的
- Python
多处理
模块包含有用的抽象,其接口非常类似于threading.Thread
- 必须使用cPython才能进行CPU绑定处理
欺骗
- IPC稍微复杂一些,开销更大(通信模型与共享内存/对象)
- 更大的内存占用
穿线
赞成的意见
- 轻量级-低内存占用
- 共享内存-使从另一个上下文访问状态更容易
- 允许您轻松创建响应性UI
- 正确释放GIL的cPython C扩展模块将并行运行
- I/O绑定应用程序的最佳选择
欺骗
- cPython-受GIL约束
- 不可中断/可终止
- 如果不遵循命令队列/消息泵模型(使用
队列
模块),则需要手动使用同步原语(需要决定锁定的粒度)
- 代码通常更难理解,也更难正确理解-竞争条件的可能性急剧增加
其他答案更多地集中在多线程与多处理方面,但在python中,必须考虑全局解释器锁(GIL)。当创建更多的线程(比如k个)时,它们通常不会将性能提高k倍,因为它仍将作为单线程应用程序运行。GIL是一个全局锁,它锁定所有内容,只允许使用单个内核执行单线程。在使用诸如numpy、网络、I/O等C扩展的地方,性能确实有所提高,在这些地方,大量的后台工作已经完成,GIL已经发布
因此,当使用线程时,只有一个操作系统级线程,而python创建的伪线程完全由线程本身管理,但实际上是作为单个进程运行的。抢占发生在这些伪线程之间。如果CPU以最大容量运行,您可能希望切换到多处理。
现在,对于自包含的执行实例,您可以选择池。但在数据重叠的情况下,如果需要进程通信,则应使用多处理。进程
进程可能有多个线程。这些线程可以共享内存,并且是进程中的执行单元
进程在CPU上运行,因此线程驻留在每个进程下。流程是独立运行的单个实体。如果要在每个进程之间共享数据或状态,可以使用内存存储工具,例如缓存(redis,memcache)
,文件
,或数据库
,如问题中所述,Python中的多处理是实现真正并行性的唯一真正方法多线程无法
#!/usr/bin/env python3
import multiprocessing
import threading
import time
import sys
def cpu_func(result, niters):
'''
A useless CPU bound function.
'''
for i in range(niters):
result = (result * result * i + 2 * result * i * i + 3) % 10000000
return result
class CpuThread(threading.Thread):
def __init__(self, niters):
super().__init__()
self.niters = niters
self.result = 1
def run(self):
self.result = cpu_func(self.result, self.niters)
class CpuProcess(multiprocessing.Process):
def __init__(self, niters):
super().__init__()
self.niters = niters
self.result = 1
def run(self):
self.result = cpu_func(self.result, self.niters)
class IoThread(threading.Thread):
def __init__(self, sleep):
super().__init__()
self.sleep = sleep
self.result = self.sleep
def run(self):
time.sleep(self.sleep)
class IoProcess(multiprocessing.Process):
def __init__(self, sleep):
super().__init__()
self.sleep = sleep
self.result = self.sleep
def run(self):
time.sleep(self.sleep)
if __name__ == '__main__':
cpu_n_iters = int(sys.argv[1])
sleep = 1
cpu_count = multiprocessing.cpu_count()
input_params = [
(CpuThread, cpu_n_iters),
(CpuProcess, cpu_n_iters),
(IoThread, sleep),
(IoProcess, sleep),
]
header = ['nthreads']
for thread_class, _ in input_params:
header.append(thread_class.__name__)
print(' '.join(header))
for nthreads in range(1, 2 * cpu_count):
results = [nthreads]
for thread_class, work_size in input_params:
start_time = time.time()
threads = []
for i in range(nthreads):
thread = thread_class(work_size)
threads.append(thread)
thread.start()
for i, thread in enumerate(threads):
thread.join()
results.append(time.time() - start_time)
print(' '.join('{:.6e}'.format(result) for result in results))
+--------------------------------------+
+ Active threads / processes +
+-----------+--------------------------------------+
|Thread 1 |******** ************ |
| 2 | ***** *************|
+-----------+--------------------------------------+
|Process 1 |*** ************** ****** **** |
| 2 |** **** ****** ** ********* **********|
+-----------+--------------------------------------+
+ Time --> +
+--------------------------------------+
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)))
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()