Python之多线程01_简介

关于多线程

线程是什么?

线程是指进程内的一个执行单元,也是进程内的可调度实体.

与进程的区别:
(1) 地址空间:进程内的一个执行单元;进程至少有一个线程;它们共享进程的地址空间;而进程有自己独立的地址空间;
(2) 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源
(3) 线程是处理器调度的基本单位,但进程不是.
(4) 二者均可并发执行.

简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

线程的切换开销小于进程,使得多线程程序的并发性高。
另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。

协程是什么?

我们知道操作系统在线程等待IO(内存、磁盘读写)的时候,会阻塞当前线程,切换到其它线程,这样在当前线程等待IO的过程中,其它线程可以继续执行。当系统线程较少的时候没有什么问题,但是当线程数量非常多的时候,却产生了问题。一是系统线程会占用非常多的内存空间,二是过多的线程切换会占用大量的系统时间

协程刚好可以解决上述2个问题。协程运行在线程之上,当一个协程执行完成后,可以选择主动让出,让另一个协程运行在当前线程之上。协程并没有增加线程数量,只是在线程的基础之上通过分时复用的方式运行多个协程,而且协程的切换在用户态完成,切换的代价比线程从用户态到内核态的代价小很多。

它们之间的关系

在这里插入图片描述

并发类型

  • CPU密集型
    在运行时比较多使用CPU进行计算和处理
    如:下载数据,压缩解压
  • I/O密集型
    在运行时比较多使用I/O(磁盘/内存)读写
    如:爬取信息,导入数据库

多线程相关技术

在这里插入图片描述

Python速度比C/C++/Java慢

据统计Python比C++慢200倍,导致现在大部分大型企业依旧使用C/C++或Java

速度慢原因

  • 原因1:
    Python是动态类型语言,边执行边解释

  • 原因2:
    存在GIL,无法使用多核CPU并发并行执行

GIL

GIL(Global Interpreter Lock,全局解释器锁)
是计算机程序语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行(无法使用多核并发)。
在这里插入图片描述
上图说 当线程在运行时它使用了GIL,当遇到I/O时释放GIL。
Thread1 获取到GIL并运行,直到遇到I/O,Thread1释放GIL。Thread2获取到GIL并运行,直到遇到I/O,Thread2释放GIL。Thread3获取到GIL并运行,直到遇到I/O,Thread3释放GIL。
同一时刻,只能有一个线程在运行。由于Python有GIL的存在,即使电脑有多核CPU,单个时刻只能使用一个1个。
相比并发加速的C++/JAVA慢的多。

为什么需要GIL

GIL是为了解决多线程之前数据完整性和状态同步问题。
案列:Python中对象的管理,是使用计数器进行的,引用数为0时则释放对象。
假设:线程1和线程2都引用对象obj,obj.ref_num = 2,线程1和线程2都想要释放obj的引用。
线程1将num自减后,num值为1,切换到线程2,并进行自减,num为0,此时线程2判断num值为0,则释放obj的引用。
当切换到线程2时,由于obj的引用已经被释放,线程2无法获取num,就会报错。或者线程2可能将其他对象给释放,导致内存混乱。

规避GIL限制

  • 在I/O密集期,多线程threading在I/O期间会释放GIL,实现I/O和CPU并行。
  • 使用multiprocessing模块多进程,可利用多核CPU并发运行,可加快CPU密集的处理。

多线程与单线程效率之比

针对博客园网站进行网站请求,以单线程与多线程运行时间进行比较:
页数:50页
电脑配置:6核12线程
单线程花费:8.815296173095703s
多线程花费:0.45113587379455566s
粗略的算了算有19倍

import requests
import threading
import time

urls =[
    f"https://www.cnblogs.com/#p{page}"
    for page in range(1,50+1)
]
#请求网页
def craw(u1):
    re = requests.get(u1)
    print(re.url,len(re.text))
    
# 单线程请求
def singleCatch():
    print("******singleCatch Start*******")
    for i in urls:
        craw(i)
    print("******singleCatch End*******")

# 多线程请求
def ThreadCatch():
    print("******ThreadCatch Start*******")
    threads=[]
    for i in urls:
        threads.append(
            threading.Thread(target=craw,args=(i,))		#给craw函数传参,使用args=(x,y)
        )
	
	#启动多线程
    for thread in threads:
        thread.start()
	#等待多线程停止
    for thread in threads:
        thread.join()
    print("******ThreadCatch End*******")

if __name__ == "__main__":
    start = time.time()
    singleCatch()
    end = time.time()
    print("singleCatch time is:",end-start)

    start = time.time()
    ThreadCatch()
    end = time.time()
    print("ThreadCatch time is:",end-start)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值