python进阶--多线程/多进程/协程

多线程/多进程/协程对比

  • 进程是资源分配的单位
  • 线程是操作系统调度的单位
  • 进程切换需要的资源很大,效率很低
  • 线程切换需要的资源一般,效率一般
  • 协程切换需要的资源很小,效率高
  • 协程因为是在一个线程中执行,所以只能是并发

运行比较

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

猜你喜欢

转载自blog.csdn.net/qq_25672165/article/details/111771373