python爬虫之多线程介绍(一)

1. 多线程简介

1.1 进程与线程

此部分的学习参考自:进程与线程

(1)进程:进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。
其任务调度采用的是时间片轮转的抢占式调度方式,进程是任务调度的最小单位,所以每个进程有各自独立的一块内存,使得各个进程之间内存地址相互隔离。
但其对CPU的要求越来越高,进程之间的切换开销较大,已经无法满足越来越复杂的程序的要求了,于是就发明了线程。

(2)线程:线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元,是处理器调度和分派的基本单位。一个进程可以有一个或多个线程,各个线程之间共享程序的内存空间(也就是所在进程的内存空间)。

(3)进程和线程的区别:
1)线程是程序执行的最小单位,而进程是操作系统分配资源的最小单位;
2)一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
3)进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段,数据集,堆等)及一些进程级的资源(如打开文件和信号等),某进程内的线程在其他进程不可见;
4)调度和切换:线程上下文切换比进程上下文切换要快得多
在这里插入图片描述
在这里插入图片描述

1.2 多线程

(1)定义
正如上文所言,线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务,多线程就是在一个进程中的多个线程,如果使用多线程默认开启一个主线程
举例:有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的。在这里开车是一个进程,用手控制方向盘和用脚控制油门就是线程。
(2)特点
1)Python 在任意时刻,只有一个线程在解释器中运行(对Python 虚拟机的访问由全局解释器锁(GIL)来控制,正是这个锁能保证同一时刻只有一个线程在运行。)
2)多线程共享主进程的资源,所以可能还会改变其中的变量,这个时候就要加上线程锁,每次执行完一个线程在执行下一个线程
3)因为每次只能有一个线程运行,多线程怎么实现的呢?Python解释器中一个线程做完了任务然后做IO(文件读写)操作的时候,这个线程就退出,然后下一个线程开始运行,循环之。(简而言之,在对一个线程的文件读写过程时,另一个线程就在执行)
(3)多线程在爬虫中的应用
多线程常用在那些需要等待然后执行的应用程序上(比如爬虫读取到数据,然后保存的时候下一个线程开始启动)也就是说多线程适用于IO密集型的任务量(文件存储,网络通信)。

'''
多线程的创建
多线程是多任务的交替同步进行
常态下是一个任务进行完毕,执行下一个任务
'''

import threading
import time

def singe():
    for i in range(3):
        print('正在唱歌....')
        time.sleep(1)

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

def main():

    t1 = threading.Thread(target=singe)
    t2 = threading.Thread(target=dance)
    t1.start()
    t2.start()

# 常态下是一个任务进行完毕,执行下一个任务
# if __name__ == '__main__':
#     singe()
#     dance()
# 结果为:
# 正在唱歌....
# 正在唱歌....
# 正在唱歌....
# 正在跳舞....
# 正在跳舞....
# 正在跳舞....

# 多线程是多任务的交替同步进行
if __name__ == '__main__':
    main()

# 结果为
# 正在唱歌....
# 正在跳舞....
# 正在唱歌....
# 正在跳舞....
# 正在唱歌....
# 正在跳舞....

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

- 主线程会等待子线程结束之后在结束
'''
threading的核心用法
 threading.Thread(target='触发的函数',name='线程的名字',args='函数传参')
 有一个参数就是用args=(i,)一定要加上逗号,如果有两个或者以上的参数就不用这样。
 start() 方法开启子线程。运行多次 start() 方法代表开启多个子线程。

'''
import threading
import time

# 定义一个函数
def loop():
    print('thread %s is running...' % threading.current_thread().name)  #threading.current_thread().name就是当前线程的名字  在声明线程的时候可以自定义子线程的名字
    n = 0
    while n < 10:
        n = n + 1
        print('%s >>> %s' % (threading.current_thread().name, n)) # 输出当前线程名字  和循环的参数n
    print('thread %s ended.' % threading.current_thread().name)  # 所有子线程执行完毕

# 下面的一部分就是threading的核心用法
# target=你定义的函数名
t = threading.Thread(target=loop, name='线程名:') # target=你定义的函数名  # name为 申明了这个线程的名字
t.start() # 开始,创建线程并执行线程
print('thread %s ended.' % threading.current_thread().name)

# thread MainThread is running...
# thread 线程名: is running...
# 线程名: >>> 1
# 线程名: >>> 2
# 线程名: >>> 3
# 线程名: >>> 4
# 线程名: >>> 5
# 线程名: >>> 6
# 线程名: >>> 7
# 线程名: >>> 8
# 线程名: >>> 9
# 线程名: >>> 10
# thread 线程名: ended.
# thread MainThread ended.

3. join和setDaemon的使用

  • join() 等待子线程结束之后,主线程继续执行。
    在多线程中,每个线程自顾执行自己的任务,当最后一个线程运行完毕后再退出,所以这个时候如果你要打印信息的话,会看到打印出来的信息错乱无章,有的时候希望主线程能够等子线程执行完毕后在继续执行,就是用join()方法。

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

import threading
import time
'''
- join() 等待子线程结束之后,主线程继续执行。
- setDaemon() 守护主线程,不会等待子线程结束。
'''
def demo():  # 子线程
    for i in range(5):
        print('hello world')
        time.sleep(1)

# if __name__ == '__main__':
#     t = threading.Thread(target=demo)
#     t.start()  # 创建线程并启动子线程
#     print(1)  # 运行一次子线程,然后运行主线程,再运行剩下的主线程

# 结果为:
# hello world
# 1
# hello world
# hello world
# hello world
# hello world

# if __name__ == '__main__':
#     t = threading.Thread(target=demo)
#     t.start()  # 创建线程并启动子线程
#     t.join()  # 等待子线程结束之后,主线程继续执行。
#     print(1)
# 结果为:
# hello world
# hello world
# hello world
# hello world
# hello world
# 1

if __name__ == '__main__':
    t = threading.Thread(target=demo)
    t.setDaemon(True) # 保护主线程,子线程执行一次完毕直接执行主线程,然后停止
    t.start()  # 创建线程并启动子线程
    print(1)

# 结果为:
# hello world
# 1

下面是搬运Python threading 多线程模块的一个案例

import threading
import time

t00 = time.time()
# 获取当前时间戳
def cs1():
    time0 = time.time()
    for x in range(5):
        print( x + time.time()-time0)
        # 计算用了多少时间
        print( threading.current_thread().name)
        # 打印这个线程名字

def cs2():
    for x1 in range(6,9):
        print( x1)
        print( threading.current_thread().name)

threads=[]
# 定义一个空的列表
t1 = threading.Thread(target=cs1)
t2 = threading.Thread(target=cs2)
threads.append(t1)
threads.append(t2)
# 把这两个线程的任务加载到这个列表中
for x in threads:
    x.start()
    # 然后执行,这个案例很常用,就是有多个函数要多线程执行的时候用到
    # 如果一个程序有多个函数,但是你只想其中的某一个或者某两个函数多线程,用法一样加入空的列表即可
    x.join()
    #线程堵塞 先运行第一个(Thread-1)再运行第二个(Thread-2)
#x.join()
#注意你的join放在这里是没有意义的,和不加join一样。线程不堵塞  但是会出现不匀称的表现  并且会修改不同线程中的变量
print('use time.{}'.format(time.time()-t00))

4.查看线程数量

'''
查看线程数量 threading.enumerate()
'''
import threading
import time

def demo1():
    for i in range(2):
        print('demo1--%d'%i)
        time.sleep(2)

def demo2():
    for i in range(4):
        print('demo2--%d' % i)
        time.sleep(2)

def main():
    t1 = threading.Thread(target=demo1,name = 'demo1')
    t2 = threading.Thread(target=demo2,name = 'demo2')
    t1.start()
    t2.start()
    while True:
        print(threading.enumerate())
        if len(threading.enumerate()) <= 1:
            break
        time.sleep(1)

if __name__ == '__main__':
    main()

'''
结果:
demo1--0
demo2--0
[<_MainThread(MainThread, started 20436)>, <Thread(demo1, started 13228)>, <Thread(demo2, started 22800)>]
[<_MainThread(MainThread, started 20436)>, <Thread(demo1, started 13228)>, <Thread(demo2, started 22800)>]
[<_MainThread(MainThread, started 20436)>, <Thread(demo1, started 13228)>, <Thread(demo2, started 22800)>]
demo2--1
demo1--1
[<_MainThread(MainThread, started 20436)>, <Thread(demo1, started 13228)>, <Thread(demo2, started 22800)>]
demo2--2
[<_MainThread(MainThread, started 20436)>, <Thread(demo2, started 22800)>]
[<_MainThread(MainThread, started 20436)>, <Thread(demo2, started 22800)>]
[<_MainThread(MainThread, started 20436)>, <Thread(demo2, started 22800)>]
demo2--3
[<_MainThread(MainThread, started 20436)>, <Thread(demo2, started 22800)>]
[<_MainThread(MainThread, started 20436)>]
'''

5.通过类创建线程

'''
通过类创建线程
'''
import threading
import time

class Mythread(threading.Thread):
    def __init__(self,num):  #  num是子线程函数的参数
        super().__init__()
        self.num = num

    def run(self): # 定义每个线程运行的函数
        print('running on numbers: %s' % self.num)
        time.sleep(3)

if __name__ == '__main__':
    t1 = Mythread(1)
    t2 = Mythread(2)
    t1.start()
    t2.start()
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值