爬虫学习--14.进程与线程

什么是进程?

电脑中时会有很多单独运行的程序,每个程序有一个独立的进程,
而进程之间是相互独立存在的。比如下标中的QQ播放器、小鹅通等等。

什么是线程?

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

什么是多进程?

同理,多进程就是指计算机同时执行多个进程,一般是同时运行多个软件。

什么是多线程?

提到多线程这里要说两个概念,就是串行和并行搞清楚这个,我们才能更好地理解多线程。
​
串行:
所谓串行,其实是相对于单条线程来执行多个任务来说的,
我们就拿下载文件来举个例子:当我们下载多个文件时,在串行中它是按照一定的顺序去进行下载的,
也就是说,必须等下载完A之后才能开始下载B,它们在时间上是不可能发生重叠的。
​
并行:
下载多个文件,开启多条线程,多个文件同时进行下载,
这里是严格意义上的,在同一时刻发生的,并行在时间上是重叠的。
​
## ==简单了解了这两个概念之后,我们再来说说到底什么什么是多线程?==
​
举个例子,我们打开腾讯管家,腾讯管家本身就是一个程序,也就是说它就是一个进程,它里面有很多的功能,我们可以看下图,能查杀病毒、清理垃圾、电脑加速等众多功能。
​
按照单线程来说,无论你想要清理垃圾、还是病毒查杀,那么你必须先做完其中的一件事,才能做下一件事,这里面是有一个执行顺序的。
​
如果是多线程的话,我们其实在清理垃圾的时候,还可以进行查杀病毒、电脑加速等等其他的操作,这个是严格意义上的同一时刻发生的,没有执行上的先后顺序。
​
**简单理解为:多线程就是指一个进程中同时有多个线程正在执行。**

多线程爬虫

由于外部网络不稳定,在使用单线程爬取网页数据时,如果有一个网页响应速度慢或者卡住了,那整个程序都要等待下去,这显然是无效率的。因此,我们可以使用多线程、多进程、协程技术来实现并发下载网页。 那么,在Python中多线程、多进程和协程应该如何选择呢? 一般来说,多进程适用于CPU密集型的代码,例如各种循环处理、大量的密集并行计算等。多线程适用于I/O密集型的代码,例如文件处理、网络交互等。协程无需通过操作系统调度,没有进程、线程之间的切换和创建等开销,适用于大量不需要CPU的操作,例如网络I/O等。 实际上,限制爬虫程序发展的瓶颈就在于网络I/O,原因是网络I/O的速度赶不上CPU的处理速度。结合多线程、多进程和协程的特点和用途,我们一般采用多线程和协程技术来实现爬虫程序。

多任务基本介绍

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

程序中模拟多任务

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()

多线程的创建

通过函数来创建

1 通过函数来创建
通过threading模块当中的一个Thread类,有一个target参数。这个参数需要我们传递一个函数对象。这个函数就可以实现多线程的逻辑
def Demo01():
    print('hello 子线程')
​
​
if __name__ == "__main__":
    for i in range(5):
        t = threading.Thread(target=Demo01)
        time.sleep(1)
        t.start()

通过类来创建

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

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

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

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

  • setDaemon() 守护线程,不会等待子线程结束

3 主线程和子线程的执行关系
主线程会等子线程结束之后再结束!考试例子画图
打印的结果有很多种 可以来猜一猜?
def Demo01():
    for i in range(5):
        print('hello 子线程')
        time.sleep(1)
​
​
if __name__ == "__main__":
    t = threading.Thread(target=Demo01)
    t.setDaemon(True)  # 守护线程,不会等待子线程结束
    t.start()
    # 第一种sleep(5秒)
    # t.join() # 等待子线程结束之后,主线程继续执行
    print(123)

查看线程数量

threading.enumerate()    查看当前线程的数量

# 查看线程数量会用到enumerate()方法 enumerate() 函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
# name = ["岳岳1", '岳岳2', '岳岳3']
# for index, i in enumerate(name):
#     print(index, i)

import threading
import time
def demo1():
    for i in range(5):
        print(f"demo1---{i}")
        time.sleep(1)

def demo2():
    for i in range(10): # 作为区别将5改成10
        print(f"demo2---{i}")
        time.sleep(1)


def main():
    t1 = threading.Thread(target=demo1)
    t2 = threading.Thread(target=demo2)
    t1.start()
    t2.start()
    # print(threading.enumerate())   # 线程是存活的
    while True:
        print(threading.enumerate())
        if len(threading.enumerate()) <= 1:
            break
        time.sleep(1)


if __name__ == '__main__':
    main()
 

验证子线程的创建与执行

# 验证子线程的创建与执行
def demo3():
    for i in range(5):
        print(f"demo1---{i}")
        time.sleep(1)


def main():
    print(threading.enumerate())
    t1 = threading.Thread(target=demo3)  #  这里并没有创建线程
    print(threading.enumerate())
    t1.start() # 当我们的调用start方法之后才成功的创建了这个子线程
    print(threading.enumerate())

if __name__ == '__main__':
    main()

线程中的资源竞争问题

# a = 20
#
#
# def fun1():
#     # 希望在函数内部修改全局变量的值  那就需要使用到一个关键字 global
#     global a
#     a = 10
#     print("函数内部:a = ", a)
#
#
# fun1()
#
# print('函数外部: a = ', a)

import threading
import time

# num = 100
#
#
# def demo1():
#     global num
#     num += 1
#     print(f'demo1--num--{num}')
#
#
# def demo2():
#     print(f'demo2--num--{num}')
#
#
# def main():
#     t1 = threading.Thread(target=demo1)
#     t2 = threading.Thread(target=demo2)
#     t1.start()
#     t2.start()
#     print(f'main--num--{num}')
#
#
# if __name__ == '__main__':
#     main()

import threading
import time
num = 0


def demo1(nums):
    global num
    for i in range(nums):
        num += 1
    print(f'demo1--num--{num}')


def demo2(nums):
    global num
    for i in range(nums):
        num += 1
    print(f'demo2--num--{num}')


def main():
    t1 = threading.Thread(target=demo1, args=(1000000,))
    t2 = threading.Thread(target=demo2, args=(1000000,))
    t1.start()
    t2.start()
    time.sleep(1)
    print(f'main--num--{num}')


if __name__ == '__main__':
    main()

  • 25
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
学习爬虫时,可以将内容划分为以下小块,每天学习一点: 1. Python基础知识:学习Python的语法、数据类型、控制流等基本知识,确保对Python编程有一定的掌握。 2. 网络基础:了解HTTP协议、URL结构、请求和响应等基础知识,这对于理解爬虫工作原理非常重要。 3. 网页解析库:学习使用网页解析库,如BeautifulSoup和lxml,掌握如何解析HTML和XML文档,提取所需数据。 4. 网络请求库:学习使用网络请求库,如requests和urllib,掌握如何发送HTTP请求、处理Cookie和Headers等操作。 5. 数据存储:学习将爬取到的数据存储到本地文件或数据库中,了解如何使用Python操作文件和数据库。 6. 动态网页爬取:学习使用Selenium或Scrapy等工具,爬取动态生成的网页内容,如JavaScript渲染的页面。 7. 反爬虫机制应对:了解常见的反爬虫技术和对策,学习如何应对封IP、验证码、反爬虫策略等问题。 8. 并发与异步:学习使用多线程、多进程或异步编程来提高爬虫的效率和性能。 9. 实战项目:通过完成一些实际的爬虫项目,如爬取特定网站的新闻、商品信息等,将所学知识应用到实践中。 确保每天学习一点,并进行实际的练习和项目实践,逐步提高自己的爬虫技能。记得及时复习和总结所学内容,加深理解。同时,保持持续学习的态度,了解最新的爬虫技术和趋势。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

F——

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值