多线程、多进程学习笔记

多线程:theading,CPU不会等待IO完成(并发)

多进程:multiprocessing,利用多核CPU的能力,真正的并行执行任务

异步IO:asyncio,在单线程中利用CPU和IO同时执行的原理,实现函数的异步执行

在使用并行编程时:

使用Lock对数据源加锁,防止冲突访问


使用Queue实现不同线程或进程之间的数据通信,实现生产者-消费者模式。

        Queue是python中的线程安全的队列实现,提供了一个适用于多线程先进先出的数据结构(队列),用来在生产者和消费者线程之间的信息传递。

        maxsize--用于定义队列中的数量上限(int)一旦达到上限,插入会被阻塞,直至队列中的数据被消费掉,如果maxsize<=0,队列大小无上限

        FIFO -- First in First Out,先进先出。Queue.Queue(maxsize=0)

        LIFO--Last in First Out,后进先出。 Queue.LifoQueue(maxsize=0)

        优先级队列。Queue.PriorityQueue(maxsize=0)

        队列的代码详解:

import queue  # python2引入包使用 import Queue

q1 = queue.Queue()  # FIFO--先进先出
q2 = queue.LifoQueue()  # LIFO--后进先出
q3 = queue.PriorityQueue()  # 优先级队列

# 生产者
for i in range(5):
    q1.put(i)
    q2.put(i)
l_demo = [2,4,1,6,3]
for l in l_demo:
    q3.put(l)

# 消费者
def customer(q):
    ls = []
    while not q.empty():  # 判断列表是否为空,为空返回True,不为空返回False
        ls.append(q.get())
    print(ls)
print('===FIFO--先进先出===')
customer(q1)
print('===LIFO--后进先出===')
customer(q2)
print('===优先级队列===')
customer(q3)

结果如下

 


使用线程池Pool(或进程池Pool),简化线程(或进程)任务


使用subprocess启动外部程序的进程。

        subprocess 模块可以启动一个新进程,并连接到它们的输入/输出/错误管道,获取进程执行的结果(状态码)。其缺点在于默认提供的父子进程之间的通信手段只有管道,同时创建的子进程(Popen)专门用来执行外部的程序或者是命令


CPU密集型

又叫做计算机密集型,是指I/O(硬盘/内存)的读写操作在很短时间内就可以完成,CPU需要大量的计算和处理。

CPU占用率高,全速运转,所以就需要IO的读写要快。

CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那些。

IO密集型

系统运作的大部分时间是CPU在等IO的读写操作,CPU的占用率较低,导致线程空余的时间多,所以此时要在CPU等待过程中启用其他线程去继续使用CPU,提高其使用效率

线程中等待时间占比较多,就要启用较多的线程,提高CPU的使用率;反之,若CPU使用时间占比较高,就要少启用几个线程

在IO密集型中通常会使用大量的外部数据源。

总结:

CPU密集型,对硬盘等读写操作并不会很频繁,主要是CPU处理一些较为复杂的工作,占比率大

IO密集型,对硬盘等读写操作较为频繁,CPU在处理数据是常会等待IO读写操作结束,比较耗时,CPU的使用率低


多线程:

优点:相对进程更轻量级,占用资源少

缺点:相比于进程,多线程只有并发执行,只能使用一个CPU;

           相比于协程,启动数目有限制,占用内存资源,有线程切换开销;

使用场景:IO密集型计算、同时运行的任务数目要求不多

使用线程池的好处:

1.提升性能:因为减去了大量新建、终止线程的开销,重用了线程资源;

2.适用场景:适合处理突发性大量请求或需要大量线程完成任务、但实际任务处理时间较短;

3.防御功能:能有效避免系统因为创建线程过多,而导致系统符合过大相应变慢等问题;

4.代码优势:使用线程池的语法比自己新建线程执行线程更加简洁。

import threading
glock = threading.Lock()
glock.acquire()  # 加锁
# 进行读写操作
glock.release()  # 释放锁

加锁阻塞线程,防止其他线程同时访问同一个数据源从而出现问题,当前线程访问完毕后,释放锁,使得下一个线程正常执行

while True,循环执行当前线程。线程池中有x个线程,就执行x个任务,但每一个任务都可以循环执行多次,以此实现代码的完整性。

多进程

优点:可以利用多核CPU并行运算

缺点:占用资源最多、可启用数目比线程少

适用场景:CPU密集型计算

进程池中有x个进程,则先同时执行列表中的x个任务,等某一个任务结束后,再执行列表中的下一个任务。

多协程

优点:内存开销最少、启动协程数量最多

缺点:支持的库有限制(requests无法使用协程,如果想用协程进行爬虫可以使用aiohttp)、代码实现复杂

适用场景:IO密集型计算、需要超多任务运行、但有现成库支持的场景

总结:

当要执行的任务属于CPU密集型时,使用多进程;

当要执行的任务时IO密集型时,则考虑一下几点问题:

是否需要超多任务量,是否有协程库支持,协程实现的代码复杂度是否可以接受

如果“是”,则选择使用多协程

如果“否”,则选择多线程


对于多线程和多进程中,提到的并行和并发

并发是指一个处理器同时处理多个任务

并行是指多个处理器或者是多核处理器同时处理多个不同的任务

并发--逻辑上的同时进行,带还是会有一个先后顺序,例如一个人要在一段时间内处理n件事

并行--实际中的同时进行、同时发生,比如3个人处理3同时处理三件事

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值