社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
使用线程的方式不能很好的使用多核cpu的能力,始终是单核工作。
使用进程可以利用多核CPU的优势。
写一个线程池:
import threading
import random
my_sum = []
def compute():
my_sum.append(sum([random.randint(1, 100) for _ in range(1000000)]))
workers = [threading.Thread(target=compute) for _ in range(8)]
for worker in workers:
worker.start()
worker.join() # 等待每一个线程执行完成
print("Results: %s" % my_sum)
# 命令行执行time python my_thread.py 可以查看当前程序执行时间。
Results: [50506721, 50489343, 50504450, 50496305, 50482675, 50486201, 50500850, 50487435]
real 0m10.992s 真实时间
user 0m10.988s 开启的线程
sys 0m0.113s 主线程
写一个进程池:
import multiprocessing
import random
def compute(n):
return sum(
[random.randint(1, 100) for i in range(1000000)])
pool = multiprocessing.Pool(processes=8)
print(pool.map(compute, range(8))) # map函数必须传入迭代区间
# 命令行执行time python my_process.py 查看程序执行时间
[50479564, 50479179, 50506536, 50516956, 50512819, 50474797, 50476164, 50509185]
real 0m2.783s 真实时间
user 0m18.542s
sys 0m0.061s
PS:通过时间比较,我们看到,进程池的性能远远大于线程池。
import threading
import time
def foo(name):
print('执行。。。')
time.sleep(3)
print('执行结束。。', name)
threading.Thread(target=foo, args=('乔布斯',)).start()
print('主线程')
打印结果:
执行。。。
主线程
执行结束。。 乔布斯
# -----------------------------------------
import multiprocessing
import time
def foo(name):
print('执行。。。')
time.sleep(3)
print('执行结束。。', name)
multiprocessing.Process(target=foo, args=('詹姆斯',)).start()
print('主进程')
打印结果:
主进程
执行。。。
执行结束。。 詹姆斯
总结:进程开启多个核(多个cpu)进行,线程仅仅是在单核模式下进行cpu的调度。
from multiprocessing import Process, Queue #Queue是进程排列
def f(test):
test.put('22') #通过创建的子进程往队列添加数据,实线父子进程交互
if __name__ == '__main__':
q = Queue() #父进程
q.put("11")
p = Process(target=f, args=(q,)) #子进程
p.start()
p.join()
print("取到:",q.get_nowait())
print("取到:",q.get_nowait())
#父进程在创建子进程的时候就把q克隆一份给子进程
#通过pickle序列化、反序列化,来达到两个进程之间的交互
结果:
取到: 11
取到: 22
from multiprocessing import Process, Pipe
def f(conn):
conn.send('11')
conn.send('22')
print("from parent:",conn.recv())
print("from parent:", conn.recv())
conn.close()
if __name__ == '__main__':
parent_conn, child_conn = Pipe() #生成管道实例,可以互相send()和recv()
p = Process(target=f, args=(child_conn,))
p.start()
print(parent_conn.recv()) # prints "11"
print(parent_conn.recv()) # prints "22"
parent_conn.send("33") # parent 发消息给 child
parent_conn.send("44")
p.join()
from multiprocessing import Manager, Process
import random
def compute(l):
l.append(sum([random.randint(1, 100) for _ in range(1000000)]))
with Manager() as manage:
l = manage.list()
processes = [Process(target=compute, args=(l,)) for _ in range(8)]
for process in processes:
process.start()
process.join()
print(l)
进程池内部维护一个进程序列,当使用时,则去进程池中获取一个进程,如果进程池序列中没有可供使用的进程,那么程序就会等待,直到进程池中有可用进程为止。
进程池中有以下几个主要方法:
from multiprocessing import Process, Pool
import time
import os
def foo(i): # 操作函数
time.sleep(2)
print(i, os.getpid(), 'foo')
return i * 100
def bar(args): # 回调函数
print('execute:', args, os.getpid())
print('主进程', os.getpid())
pool = Pool(processes=3) # 可以修改进程数目
for i in range(10):
pool.apply_async(func=foo, args=(i,), callback=bar) # 回调函数的形参是调用函数的返回值
pool.close()
pool.join()
协程(Coroutine) : 是单线程下的并发 , 又称微线程 , 纤程 . 协程是一种用户态的轻量级线程 , 即协程有用户自己控制调度
协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。
协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态
使用协程的优缺点
优点 :
缺点 :
# ------------------------------
from greenlet import greenlet
def foo():
print(11)
g2.switch()
print(12)
g2.switch()
def fo():
print(21)
g1.switch()
print(22)
g1 = greenlet(foo)
g2 = greenlet(fo)
g1.switch()
# --------------------------
# Gevent 是一个第三方库,可以轻松
# 通过gevent实现并发同步或异步编程,
# 在gevent中用到的主要模式是Greenlet,
# 它是以C扩展模块形式接入Python的轻量级协程。
# TODO 加一个monkey.patch_all() 就可以使用time.sleep()去替换gevent.sleep(),达到IO耗时操作处理
import gevent
def f1():
print('Runing f1...')
gevent.sleep(3)
print('最慢')
def f2():
print('Runing f2...')
gevent.sleep(2)
print('中')
def f3():
print('Runing f3')
gevent.sleep(1)
print('快')
gevent.joinall([
gevent.spawn(f1),
gevent.spawn(f2),
gevent.spawn(f3),
])
# Runing f1...
# Runing f2...
# Runing f3
# 快
# 中
# 最慢
# ---------------------------
import requests
import gevent
import re
def get_page(page, result_list):
url = 'http://maoyan.com/board/4?offset=' + str(page)
headers = {
"User-Agent": "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)"
}
response = requests.get(url, headers=headers)
if response.status_code == 200:
html = response.content.decode('utf-8')
movie_names = parse_page(html)
result_list.append(movie_names)
def parse_page(html):
# 片名
pattern = re.compile('movieId.*?>.*?<img.*?<img.*?alt="(.*?)" class.*?', re.S)
movie_names = re.findall(pattern, html)
return movie_names
result_list = []
gevent_list = [gevent.spawn(get_page, page * 10, result_list) for page in range(10)]
gevent.joinall(gevent_list)
print(result_list)
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!