多线程与多进程效率比较
- 多线程/多进程/协程对比
- 运行比较
- 多线程
- 多进程
- 协程
- 总结
- 组合使用
多线程/多进程/协程对比
- 进程是资源分配的单位
- 线程是操作系统调度的单位
- 进程切换需要的资源很大,效率很低
- 线程切换需要的资源一般,效率一般
- 协程切换需要的资源很小,效率高
- 协程因为是在一个线程中执行,所以只能是并发
运行比较
1000个任务分别使用3个线程运行和3个进程运行。比较运行时间
多线程
由于python GIL的原因,导致无法真正并行,只能进行并发,每次仅有一个线程运行
import queue
import requests
import threading
import time
# 创建线程队列并新增1000个任务
q = queue.Queue()
for i in range(1000):
q.put('http://127.0.0.1:5000')
def work():
while q.qsize() > 0:
url = q.get()
requests.get(url)
if __name__ == "__main__":
st = time.time()
p1 = threading.Thread(target=work)
p2 = threading.Thread(target=work)
p3 = threading.Thread(target=work)
p1.start()
p2.start()
p3.start()
p1.join()
p2.join()
p3.join()
t = time.time() - st
print(t)
>
3.8531129360198975
多进程
python多进程可以实现并行和并发
import requests
from multiprocessing import Pool, Manager
import time
count = 0
# 需要将q作为参数传入
def work(q):
while q.qsize() > 0:
url = q.get()
requests.get(url)
global count
count += 1
print(count)
if __name__ == "__main__":
st = time.time()
# 创建进程池队列并新增1000个任务
q = Manager().Queue()
for i in range(1000):
q.put('http://127.0.0.1:5000')
pool = Pool(3)
for i in range(3):
pool.apply_async(work, args=(q,))
pool.close()
pool.join()
t = time.time() - st
print(t)
>
2.9781038761138916
协程
import queue
import requests
import time
import gevent
from gevent import monkey
# 创建队列并新增1000个任务
q = queue.Queue()
for i in range(1000):
q.put('http://127.0.0.1:5000')
def work():
while q.qsize() > 0:
url = q.get()
requests.get(url)
time.sleep(0)
if __name__ == "__main__":
st = time.time()
p1 = gevent.spawn(work)
p2 = gevent.spawn(work)
p3 = gevent.spawn(work)
p1.join()
p2.join()
p3.join()
t = time.time() - st
print(t)
>
4.807593822479248
总结
- 在上述条件下比较得出,多进程的效率更高一下。但我们不能无限制的使用多进程,因为太耗费资源。
- 多任务时,优先考虑协程
组合使用
- 10000个任务,使用2个进程,每个进程3个线程,每个线程5个协程来处理这些任务
import gevent
import time
import requests
import queue
from threading import Thread
from multiprocessing import Process, Queue
def process_work(q, p_name):
thread_list = []
for i in range(1, 4):
t_name = '{}-th-{}'.format(p_name,i)
print('创建线程{}------'.format(t_name))
t = Thread(target=thread_work, args=(q, t_name))
thread_list.append(t)
t.start()
for t in thread_list:
t.join()
def thread_work(q, t_name):
g_list = []
for i in range(1, 6):
g_name = '{}-g-{}'.format(t_name, i)
print('创建协程{}------'.format(g_name))
g = gevent.spawn(green_work, q, g_name)
g_list.append(g)
gevent.joinall(g_list)
def green_work(q, g_name):
count = 0
while not q.empty():
try:
url = q.get(timeout=0.01)
requests.get(url)
gevent.sleep(0.001)
count += 1
except queue.Empty:
break
print('----协程{}执行了{}个任务----'.format(g_name, count))
def count_time(func):
def wrapper(*args, **kwargs):
print('开始执行')
st = time.time()
res = func(*args, **kwargs)
et = time.time()
print('结束执行')
print('总耗时:{}'.format(et - st))
return res
return wrapper
@count_time
def main():
q = Queue()
for i in range(10000):
q.put('http://127.0.0.1:5000/')
# print('队列创建完成,数量:{}'.format(q.qsize())) # mac下运行q.qsize()会报错:NotImplementedError
print('队列创建完成')
pro_list = []
for i in range(1,3):
p_name = 'pro-{}'.format(i)
print('创建进程{}'.format(p_name))
p = Process(target=process_work, args=(q, p_name)) # 进程间的Queue()需要当作参数传入
p.start()
pro_list.append(p)
for p in pro_list:
p.join()
if __name__ == '__main__':
main()
>
开始执行
队列创建完成
创建进程pro-1
创建进程pro-2
创建线程pro-1-th-1------
创建线程pro-2-th-1------
创建协程pro-1-th-1-g-1------
创建协程pro-2-th-1-g-1------
创建线程pro-1-th-2------
创建线程pro-2-th-2------
创建协程pro-1-th-2-g-1------
创建协程pro-2-th-2-g-1------
创建线程pro-1-th-3------
创建线程pro-2-th-3------
创建协程pro-1-th-3-g-1------
创建协程pro-2-th-3-g-1------
创建协程pro-1-th-1-g-2------
创建协程pro-2-th-1-g-2------
创建协程pro-2-th-2-g-2------
创建协程pro-1-th-1-g-3------
创建协程pro-1-th-1-g-4------
创建协程pro-1-th-1-g-5------
创建协程pro-2-th-2-g-3------
创建协程pro-2-th-2-g-4------
创建协程pro-2-th-3-g-2------
创建协程pro-1-th-2-g-2------
创建协程pro-2-th-3-g-3------
创建协程pro-1-th-2-g-3------
创建协程pro-2-th-3-g-4------
创建协程pro-2-th-3-g-5------
创建协程pro-1-th-3-g-2------
创建协程pro-1-th-3-g-3------
创建协程pro-1-th-3-g-4------
创建协程pro-1-th-3-g-5------
创建协程pro-2-th-2-g-5------
创建协程pro-2-th-1-g-3------
创建协程pro-2-th-1-g-4------
创建协程pro-2-th-1-g-5------
创建协程pro-1-th-2-g-4------
创建协程pro-1-th-2-g-5------
----协程pro-2-th-1-g-1执行了331个任务----
----协程pro-2-th-1-g-2执行了331个任务----
----协程pro-2-th-1-g-3执行了331个任务----
----协程pro-2-th-1-g-4执行了331个任务----
----协程pro-2-th-3-g-4执行了331个任务----
----协程pro-2-th-1-g-5执行了331个任务----
----协程pro-2-th-3-g-5执行了331个任务----
----协程pro-1-th-2-g-2执行了334个任务----
----协程pro-1-th-2-g-3执行了334个任务----
----协程pro-1-th-2-g-4执行了334个任务----
----协程pro-1-th-2-g-5执行了334个任务----
----协程pro-2-th-3-g-1执行了332个任务----
----协程pro-2-th-3-g-2执行了332个任务----
----协程pro-2-th-2-g-5执行了331个任务----
----协程pro-1-th-2-g-1执行了335个任务----
----协程pro-2-th-2-g-1执行了332个任务----
----协程pro-2-th-2-g-2执行了332个任务----
----协程pro-2-th-2-g-3执行了332个任务----
----协程pro-2-th-3-g-3执行了332个任务----
----协程pro-1-th-1-g-3执行了335个任务----
----协程pro-1-th-1-g-4执行了335个任务----
----协程pro-1-th-1-g-5执行了335个任务----
----协程pro-1-th-1-g-1执行了336个任务----
----协程pro-2-th-2-g-4执行了332个任务----
----协程pro-1-th-3-g-1执行了336个任务----
----协程pro-1-th-3-g-2执行了336个任务----
----协程pro-1-th-3-g-3执行了336个任务----
----协程pro-1-th-3-g-4执行了336个任务----
----协程pro-1-th-1-g-2执行了336个任务----
----协程pro-1-th-3-g-5执行了336个任务----
结束执行
总耗时:9.003000974655151