爬虫学习笔记-单线程与多线程的差别-案例演示

多线程与多进程并发爬取

堵塞与非堵塞

在编程里经常会听到堵塞,非堵塞,同步,异步等这些专业词语。那这些到底代表着什么意思呢?举个通俗点的例子来进行说明:

(1)同步堵塞:你拿个水壶去烧水,就这样在火炉前等待着水烧开,期间不去干其他事,就这样站着,每过一段时间查看一下水烧开了没有。

(2)同步非堵塞:你还是拿个水壶去烧水,但不再傻傻的站着那里等水烧开,而是跑回房间上网。每过一段时间就来查看一下水烧开了没有,没烧开就走人。

(3)异步堵塞。你这次换了个水壶,水壶烧开以后会自己响提醒你,不需要你再查看。你还是去烧水,站在那里等水壶响,这就是异步堵塞

(4)异步非堵塞。你想了一下不对,既然水壶会自己通知你水烧开了没有,就不需要在这里继续等待了,所以烧水的时候就回房间做其他事情,等待水烧开后水壶自动通知你。

在Python代码中,与用还遍历的方式去执行函数时,其实就是属于同步堵塞的模式,效率十分低下,可以试一下运行下面这段代码。

import time
start =time.time()
def hello():
    time.sleep(1)
    print('hello world')
for i in range(0,10):
    hello()
print(time.time()-start)

运行结果

D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/test/duoxianchen/tongbuduse.py
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
10.095674514770508

这段代码的运行花了10.095674514770508秒时间,因为它是使用了单线程堵塞的方式去执行函数。也就是如果有十壶水需要烧开,使用了每次只烧一壶的方式。其实可以使用异步的方式烧水,每次可以烧多壶水。

import time
import threading
start =time.time()
def hello():
    time.sleep(1)
    print("hello world")
for i in range(0,10):
    t=threading.Thread(target=hello)
    t.start()
print(time.time()-start)

执行此段代码

D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/test/duoxianchen/yibusaoshui2.py
0.0019941329956054688
hello worldhello worldhello worldhello worldhello worldhello worldhello world





hello worldhello world
hello world



Process finished with exit code 0

这结果是复制粘贴的,不是录进去的。所以中间那行两个hello world 连在一块也是代码运行的结果。这里可以看到最后花费的时间在第一行就直接打印了出来,使用时间不到一秒。那么这段代码到底是怎么实现的呢?

首先,通过for循环遍历创建10个线程:

t = threading.Thread(target=hello)    # 创建线程

然后开启线程:

t.start()

这样就开启了10个线程,并发执行hello函数,在执行hello函数的同时还执行了计算花费时间的

也就是开启线程的一刻,就结束了,

print(time.time()-start)

全部代码完成使用的时间其实只有1秒,就是我们强制执行的那1秒睡眠时间:

time.sleep(1)

但是,由于刚才执行的代码都是无效的,虽然很快,但没办法证明只用1秒完成。为了证明需要修改一下代码,不再使用循环遍历的方式创建线程:

import time
import threading
start =time.time()
def hello():
    time.sleep(1)
    print("hello world")
#for i in range(0,10):
# hello()
''' 使用手动的方式创建10个进程并执行'''
t1 = threading.Thread(target=hello)
t2 = threading.Thread(target=hello)
t3 = threading.Thread(target=hello)
t4 = threading.Thread(target=hello)
t5 = threading.Thread(target=hello)
t6 = threading.Thread(target=hello)
t7 = threading.Thread(target=hello)
t8 = threading.Thread(target=hello)
t9 = threading.Thread(target=hello)
t10 = threading.Thread(target=hello)

t1.start()
t2.start()
t3.start()
t4.start()
t5.start()
t6.start()
t7.start()
t8.start()
t9.start()
t10.start()

'''等待子线程结束'''
t1.join()
t2.join()
t3.join()
t4.join()
t5.join()
t6.join()
t7.join()
t8.join()
t9.join()
t10.join()

print(time.time()-start)

运行代码,结果如下

D:\pythonproject\venv\Scripts\python.exe D:/pythonproject/test/duoxianchen/bingfa3.py
hello worldhello worldhello world
hello worldhello worldhello worldhello worldhello world


hello worldhello world





1.007749319076538

Process finished with exit code 0

​ 总共使用时间在最后打印了出来,总花费时间比1秒多出一点。1秒是我们代码强制睡眠的时间,后面的0…007749319076538 是代码执行花费的时间。

​ 不过这个例子有点太笨,需要自己手动写10个进程去添加,再换一个例子来说明。

import threading

import time

start =time.time()
def sing():
    time.sleep(3)
    print("我喜欢唱歌")
def dance():
    time.sleep(3)
    print("我喜欢跳舞")

sing()
dance()
print(time.time()-start)

创建两个函数,分别是sing函数 dance函数,睡眠3秒后打印 “我喜欢唱歌”;然后运行 dance 函数,睡眠3秒后打印“我喜欢跳舞”.然后运行代码,结果输入如下:

我喜欢唱歌
我喜欢跳舞
6.009649276733398

使用单线程堵塞的方式运行代码,先运行sing函数然后运行dance 函数,花费时间为6.009649276733398。然后修改一下代码,使用多线程的模式。

import time
import threading
start =time.time()
def sing():
    time.sleep(3)
    print("我喜欢唱歌")
def dance():
    time.sleep(3)
    print("我喜欢跳舞")
t1 =threading.Thread(target=sing)
t2 =threading.Thread(target=dance)
t1.start()
t2.start()
t1.join()
t2.join()
print(time.time()-start)

运行代码,结果如下

我喜欢唱歌我喜欢跳舞

3.004112958908081

可以看到,运行代码只花费了3.004112958908081秒。sing函数和dance函数是同时运行并且睡眠了3秒钟,然后打印"我喜欢唱歌"和"我喜欢跳舞"所谓多线程就是分别新增多条进程去完成目标工作。可以将上面的代码修改将工作的进程打印出来。

import time
import threading
start =time.time()
def sing():
    time.sleep(3)
#	print("我喜欢唱歌")    #删除代码
    print("我喜欢唱歌,现在运行的进程是 %s"%threading.current_thread().name)#打印线程名字
def dance():
    time.sleep(3)
#   print("我喜欢跳舞")	  #删除代码
    print('我喜欢跳舞,现在运行的进程是 %s'%threading.current_thread().name)#打印线程名字
t1 =threading.Thread(target=sing)
t2 =threading.Thread(target=dance)
t1.start()
t2.start()
t1.join()
t2.join()
print(time.time()-start)

运行结果

我喜欢跳舞,现在运行的进程是 Thread-2我喜欢唱歌,现在运行的进程是 Thread-1

3.0113303661346436

可以看到,运行sing 函数和dance函数使用了两条进程,分别是Thread-1 和Thread-2。两条进程同步分别运行了 sing函数和dance函数,避免了堵塞,优化了效率,节省了时间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值