进程和线程-----爬虫

1.进程与线程:

1.1 什么是进程?

        电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,而进程之间是相互独立存在的。比如微信,QQ等等应用程序。

1.2 什么是线程?

        进程可以简单的理解为一个可以独立运行的程序单位,它是线程的集合,进程就是有一个或多个线程构成的。
        而线程是进程中的实际运行单位,是操作系统进行运算调度的最小单位。可理解为线程是进程中的一个最小运行单元。

1.3 什么是多进程?

        同理,多进程就是指计算机同时执行多个进程,一般是同时运行多个软件。就比如我在电脑上同时打开微信和QQ,此时电脑就在同时执行两个进程。

1.4 什么是多线程?

提到多线程这里要说两个概念,就是串行并行搞清楚这个,我们才能更好地理解多线程。

串行:
所谓串行,其实是相对于单条线程来执行多个任务来说的,我们就拿下载文件来举个例子:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,
也就是说,必须等下载完A之后才能开始下载B,它们在时间上是不可能发生重叠的。

并行:
下载多个文件,开启多条线程,多个文件同时进行下载,这里是严格意义上的,在同一时刻发生的,并行在时间上是重叠的。

简单了解了这两个概念之后,我们再来说说到底什么什么是多线程

举个例子,我们打开腾讯管家,腾讯管家本身就是一个程序,也就是说它就是一个进程,它里面有很多的功能,我们可以看下图,能查杀病毒、清理垃圾、电脑加速等众多功能。

按照单线程来说,无论你想要清理垃圾、还是病毒查杀,那么你必须先做完其中的一件事,才能做下一件事,这里面是有一个执行顺序的。

如果是多线程的话,我们其实在清理垃圾的时候,还可以进行查杀病毒、电脑加速等等其他的操作,这个是严格意义上的同一时刻发生的,没有执行上的先后顺序。

简单理解为:多线程就是指一个进程中同时有多个线程正在执行。

2. 多线程爬虫:

        由于外部网络不稳定,在使用单线程爬取网页数据时,如果有一个网页响应速度慢或者卡住了,那整个程序都要等待下去,这显然是无效率的。

        因此,我们可以使用多线程、多进程、协程技术来实现并发下载网页。 那么,在Python中多线程、多进程和协程应该如何选择呢? 一般来说,多进程适用于CPU密集型的代码,例如各种循环处理、大量的密集并行计算等。多线程适用于I/O密集型的代码,例如文件处理、网络交互等。

        协程无需通过操作系统调度,没有进程、线程之间的切换和创建等开销,适用于大量不需要CPU的操作,例如网络I/O等。 实际上,限制爬虫程序发展的瓶颈就在于网络I/O,原因是网络I/O的速度赶不上CPU的处理速度。结合多线程、多进程和协程的特点和用途,我们一般采用多线程和协程技术来实现爬虫程序。

3. 多任务基本介绍

        有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的。

程序中模拟多任务:

import time

def sing():
    for i in range(3):
        print("正在唱歌... %d"%i)
        time.sleep(1)

def dance():
    for i in range(3):
        print("正在跳舞...%d"%i)
        time.sleep(1)

if __name__ == '__main__':
    sing()
    dance()

        我们在该程序内添加了两个任务,分别是唱歌和跳舞,我们可以看到上面的运行结果,首先是唱歌任务进行三次之后开始进行跳舞,说明它是一个串行的任务,而我们的多线程就是将其同时进行唱歌和跳舞。

4. 多线程的创建:

4.1 通过函数来创建:

# 1 通过函数来创建
# 通过threading模块当中的一个Thread类,有一个target参数。这个参数需要我们传递一个函数对象。这个函数就可以实现多线程的逻辑
import threading
import time
def Demo01():
    print('hello 子线程')


if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=Demo01)
        time.sleep(1)
        t.start()

        我们通过一个for 循环创建了5个线程,然后通过使用threading模块中的Thread类确定我们进行处理的任务,使用start方法开始运行。

4.2 通过类来创建:

# 2 通过类来创建
# 自定义一个类,需要继承父类 threading.Thread 并重写run()方法
import threading
class Demo02(threading.Thread):
    def run(self) -> None:
        for i in range(5):
            print("hello 子线程")


if __name__ == "__main__":
    d = Demo02()
    d.start()

        我们使用类创建多线程需要继承父类的threading模块中的Thread类,然后必须重写run方法,因为run方法相当于是你这个任务的说明书,你的多线程程序是按照这个run方法进行的。

5. 主线程和子线程的执行关系:

  • 主线程会等待子线程结束之后在结束

  • join() 等待子线程结束之后,主线程继续执行

# 3 主线程和子线程的执行关系

import threading,time
def Demo01():
    for i in range(5):
        print('hello 子线程')
        time.sleep(1)


if __name__ == "__main__":
    t = threading.Thread(target=Demo01)
    t.start()
    print(123)

 

        我们对比这两次运行,发现运行结果会不一样,其实这其中就存在主线程和子线程的一个运行速度的关系,如果你的程序先进行子线程的话,那么就会打印‘hello 子线程’,而之后再打印主线程,因为主线程和子线程也是同时进行的(这里的同时指的宏观上同时,微观上不同时,可以参考操作系统进行理解),而如果你的主线程要快的话就会先进行主线程后面的内容。

        其实这就是主线程和子线程的之间的一个资源竞争,主线程和子线程同时想要获取资源就会产生这样的情况,那我我们该如何解决这个问题呢:

  1. 使用time.sleep方法,让主线程睡眠到子线程结束,其实这就是一个强行使得主线程排到子线程的后面进行一个串行操作,这样的话就不会发生主线程和子线程进行资源竞争了。
  2. 使用join方法,join方法会让主线程等待子线程结束之后,主线程会继续执行。

程序展示:

# 3 主线程和子线程的执行关系

import threading,time
def Demo01():
    for i in range(5):
        print('hello 子线程')
        time.sleep(1)


if __name__ == "__main__":
    t = threading.Thread(target=Demo01)
    t.start()
    # 解决资源竞争
    # 第一种sleep(5秒)
    # time.sleep(5)
    # 第二种join()等待子线程结束之后,主线程继续执行
    # t.join()
    print(123)

 使用任意的一个最后都能得到这个运行结果,所以资源竞争问题就被解决了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值