目标:
    1:掌握多进程以及多线程的适用场景
    2:掌握创建多线程的步骤以及常用语法
    3:掌握多线程爬虫的工作原理
    4:掌握Python中队列的一些操作!
    5:掌握多线程对全局变量加线程锁的目的

    一:多进程以及多线程的适用场景

    多进程适用场景:计算机密集型的操作(CPU密集型),比如说计算大量数据时,多进程可以保证同一时间多个进程同时工作,就是比较消耗内存

    多线程适用场景:I/O密集型操作,比如说网络I/O、本地磁盘I/O,爬虫就是网络I/O与本地磁盘I/O(存储数据)结合体,多线程因为GIL的存在同一时间只能保证有一个线程在工作,但是也能保证同意时间肯定有一个线程在工作

    二:创建多线程的步骤以及常用语法

    步骤:

    第一步:from threading import Thread 导入模块

    第二步:t = Thread(target=任务函数名,args=()) 创建线程对象 args = (),是元组给任务函数传参,相当于位置传参

    第三步:t.start() 启动线程

    第四步:time.sleep(1) 等待线程任务执行完毕

    第五步:t.join() 当 t 线程任务执行结束后 回收 t 这个线程(防止死线程的产生)

    注意事项:
    1:join()方法功能是等待创建的某线程执行任务函数结束后进行线程的回收
    2:利用join 方法时程序会进入阻塞状态,回收完执行任务完毕的线程后才解除阻塞!



    常用建立多线程语法截图

    image.png

    三:多线程爬虫的工作原理

    image.png

    四:Python中队列的一些操作【数据存入、取出、取数据阻塞等问题】!

    队列存入数据的优先级:队列中的数据是先进先出,从队尾进入,队首取出

    操作队列的步骤:

    第一步:from queue import Queue 导入队列模块

    第二步:q = Queue() 创建q这个队列

    第三步:q.put(存入队列的数据) 向q这个队列存入数据

    第四步:q.get() 从q这个队列中取出存入的数据

    第五步:q.empty() 判断队列中数据是否为空,是返回True,不是返回False

    队列取数据出现的问题:当利用 get 方法从队列中取数据时,队列数据为空,那么程序就会进入阻塞状态,有什么办法让程序不阻塞嘛?

    解决办法:
    1:q.get(timeout=2) 设置阻塞时间,当超过两秒钟还未从队列中取得数据,程序直接报错

    2:q.get(block=False) 在队列取不到数据直接报错,block默认值为True,也就是取不到数据程序一直处于阻塞状态

    3:q.get_nowait() 利用get_nowait方法取数据,队列内没有数据直接报错!!

    4:while not q.empty( ): 判断队列内是否有数据,有数据才取,没数据不取
    q.get( )


    五:多线程对全局变量加线程锁的目的以及加锁步骤

    加线程锁目的:多个线程共享(共同操作)一个全局变量时(全局文件变量、全局变量、全局队列变量),可能会产生资源竞争问题,这时就要对全局变量进行加线程锁,等一个线程对全局变量操作完全结束后,在进行线程锁的释放!,可避免多线程间资源的竞争

    加线程锁步骤:

    第一步: from threading import Lock 导入线程锁模块

    第二步:l_obj = Lock() 创建一个线程锁

    第三步:l_obj.acquire( ) 某一个线程在对全局变量操作之前进行加锁

    第四步:l_obj.release() 某一个线程在对全局变量操作完全结束后进行开锁

    注意事项:对某个全局变量上线程锁,在线程锁未开锁之前,又给这个全局变量上了一把锁,这是程序会进入阻塞状态

    图示:

    不加线程锁造成的多线程间资源竞争问题

    image.png

    加线程锁解决多线程之间的资源竞争

    image.png


    多线程爬虫共同操作全局变量【同一个资源】(文件变量、队列变量等)的注意事项:只要是线程对全局变量(同一个资源)进行了操作都要加上一把线程锁!!!

    注意事项:全局变量(全局队列等)有几个,那么就要创建几把线程锁