多进程、多线程与协程

多进程、多线程与协程

目录

  • 代码整理
    • 进程池
    • 多线程
    • 线程池
    • 协程
  • 应用场景
    • 概述
    • 确定线程池大小
    • 确定进程池大小
    • 解决方案
  • 进一步理解
    • 前言
    • 多进程&多线程
      • 概述
      • 优劣
      • 区别
    • 线程池作用&原理
      • 线程池作用
      • 线程池原理
  • 参考内容
  • 总结

一、代码整理

1.1 进程池

from multiprocessing import Pool
import os, time, random

def long_time_task(name):
    print('Run task %s (%s)...' % (name, os.getpid()))
    start = time.time()
    time.sleep(random.random() * 3)
    end = time.time()
    print('Task %s runs %0.2f seconds.' % (name, (end - start)))

if __name__=='__main__':
    print('Parent process %s.' % os.getpid())
    p = Pool(4)
    for i in range(5):
        p.apply_async(long_time_task, args=(i,))
    print('Waiting for all subprocesses done...')
    p.close()
    p.join()
    print('All subprocesses done.')

1.2 多线程

import time, threading

def loop():
    print('thread %s is running...' % threading.current_thread().name)
    n = 0
    while n < 5:
        n = n + 1
        print('thread %s >>> %s' % (threading.current_thread().name, n))
        time.sleep(1)
    print('thread %s ended.' % threading.current_thread().name)

print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)

1.3 线程池

from concurrent.futures import ThreadPoolExecutor
import urllib.request
import time

def fetch_url(url):
    u = urllib.request.urlopen(url)
    data = u.read()
    print('data', data)
    return data

pool = ThreadPoolExecutor(10)

start = time.time()
# Submit work to the pool
a = pool.submit(fetch_url, 'http://www.python.org')
b = pool.submit(fetch_url, 'http://www.pypy.org')

# Get the results back
x = a.result()
y = b.result()

end = time.time()
print('runtime %d' % (end - start))

1.4 协程

def consumer():
    r = ''
    while True:
        n = yield r
        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None)
    n = 0
    while n < 5:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

c = consumer()
produce(c)

二、应用场景

2.1 概述

多进程适合在 CPU 密集型操作(cpu 操作指令比较多,如位数多的浮点运算)。
多线程适合在 IO 密集型操作(读写数据操作较多的,比如爬虫)。
协程间是协同调度的,这使得并发量数万以上的时候,协程的性能是远远高于线程。

2.2 确定线程池大小

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

2.3 确定进程池大小

最佳线程数目 = CPU数目,即一个 CPU 执行一个进程。

2.4 解决方案

多进程 + 协程
既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。

三、进一步理解

3.1 前言

copy代码,很容易就可以参考着用,可是明白底层的原理是非常之关键的。

3.2 多进程&多线程

3.2.1 概述

进程是操作系统分配资源(比如内存)的最基本单元
线程是操作系统能够进行调度和分派的最基本单元。
多进程允许多个任务同时运行。
多线程允许将单个任务分成多个子任务运行。

3.2.2 优劣

多进程优点:稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。
多线程缺点:任何一个线程挂掉都可能直接造成整个进程崩溃,因为所有线程共享进程的内存。

3.2.3 区别

多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响。
多线程中,所有变量都由所有线程共享。

3.3 线程池作用&原理

3.3.1 线程池作用

有效的降低频繁创建销毁线程所带来的额外开销。

3.3.2 线程池原理

采用预创建的技术,在应用启动之初便预先创建一定数目的线程。应用在运行的过程中,需要时可以从这些线程所组成的线程池里申请分配一个空闲的线程,来执行一定的任务,任务完成后,并不是将线程销毁,而是将它返还给线程池,由线程池自行管理。

四、参考内容

  • 廖雪峰的Python3.x教程
  • 进程、线程和协程
  • 如何合理地估算线程池大小?

五、总结

代码谁都会写,但是底层懂才是真的懂。

你可能感兴趣的:(python学习)