python爬虫知识点:5种线程锁

关于线程安全 按照之前对的设定,第二次加的时候就会产生阻塞等待,直到第一次的被释放,才能获得到第二个,但是释放第一个也是由该线程完成的,结果线程躺平了,啥也不干,也就无法进行解这样的操作,这时就会产生死的现象。但是在java中为了很好的解决这些问题,在synchronized中引入了可重入的概念,但是在c++中不具有这样的特性很容易就产生死的现象。 阅读详情

嗨喽~大家好呀,这里是魔王呐 ❤ ~!

python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取

线程安全

线程安全是多线程或多进程编程中的一个概念,在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。

线程安全的问题最主要还是由线程切换导致的,比如:

一个房间(进程)中有10颗糖(资源),除此之外还有3个小人(1个主线程、2个子线程),当小人A吃了3颗糖后被系统强制进行休息时他认为还剩下7颗糖,而当小人B工作后又吃掉了3颗糖,那么当小人A重新上岗时会认为糖还剩下7颗,但是实际上只有4颗了。

上述例子中线程A和线程B的数据不同步,这就是线程安全问题,它可能导致非常严重的意外情况发生,我们按下面这个示例来进行说明。

下面有一个数值num初始值为0,我们开启2条线程:

  • 线程1对num进行一千万次+1的操作

  • 线程2对num进行一千万次-1的操作

结果可能会令人咋舌,num最后并不是我们所想象的结果0:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import threading

num = 0


def add():
    global num
    for i in range(10_000_000):
        num += 1


def sub():
    global num
    for i in range(10_000_000):
        num -= 1


if __name__ == "__main__":
    subThread01 = threading.Thread(target=add)
    subThread02 = threading.Thread(target=sub)

    subThread01.start()
    subThread02.start()

    subThread01.join()
    subThread02.join()

    print("num result : %s" % num)

# 结果三次采集
# num result : 669214
# num result : -1849179
# num result : -525674

上面这就是一个非常好的案例,想要解决这个问题就必须通过锁来保障线程切换的时机。

需要我们值得留意的是,在Python基本数据类型中list、tuple、dict本身就是属于线程安全的,

所以如果有多个线程对这3种容器做操作时,我们不必考虑线程安全问题。

锁的作用

锁是Python提供给我们能够自行操控线程切换的一种手段,使用锁可以让线程的切换变的有序。

一旦线程的切换变的有序后,各个线程之间对数据的访问、修改就变的可控,所以若要保证线程安全,就必须使用锁。

threading模块中提供了5种最常见的锁,下面是按照功能进行划分:

  1. 同步锁:lock(一次只能放行一个)

  2. 递归锁:rlock(一次只能放行一个)

  3. 条件锁:condition(一次可以放行任意个)

  4. 事件锁:event(一次全部放行)

  5. 信号量锁:semaphore(一次可以放行特定个)

1、Lock() 同步锁

基本介绍

Lock锁的称呼有很多,如:

  • 同步锁

  • 互斥锁

它们是什么意思呢?如下所示:

  • 互斥指的是某一资源同一时刻仅能有一个访问者对其进行访问,具有唯一性和排他性,但是互斥无法限制访问者对资源的访问顺序,即访问是无序的

  • 同步是指在互斥的基础上(大多数情况),通过其他机制实现访问者对资源的有序访问

同步其实已经实现了互斥,是互斥的一种更为复杂的实现,因为它在互斥的基础上实现了有序访问的特点

下面是threading模块与同步锁提供的相关方法:

使用方式

同步锁一次只能放行一个线程,一个被加锁的线程在运行时不会将执行权交出去,只有当该线程被解锁时才会将执行权通过系统调度交由其他线程。

如下所示,使用同步锁解决最上面的问题:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
import threading

num = 0

def add():
    lock.acquire()
    global num
    for i in range(10_000_000):
        num += 1
    lock.release()


def sub():
    lock.acquire()
    global num
    for i in range(10_000_000):
        num -= 1
    lock.release()

if __name__ == "__main__":
    lock = threading.Lock()

    subThread01 = threading.Thread(target=add)
    subThread02 = threading.Thread(target=sub)

    subThread01.start()
    subThread02.start()

    subThread01.join()
    subThread02.join()

    print("num result : %s" % num)

# 结果三次采集
# num result : 0
# num result : 0
# num result : 0

这样这个代码就完全变成了串行的状态,对于这种计算密集型I/O业务来说,还不如直接使用串行化单线程执行来得快,所以这个例子仅作为一个示例,不能概述锁真正的用途。

死锁现象

对于同步锁来说,一次acquire()必须对应一次release(),不能出现连续重复使用多次acquire()后再重复使用多次release()的操作,这样会引起死锁造成程序的阻塞,完全不动了,如下所示:

import threading

num = 0


def add():
    lock.acquire()  # 上锁
    lock.acquire()  # 死锁
    # 不执行
    global num
    for i in range(10_000_000):
        num += 1
    lock.release()
    lock.release()


def sub():
    lock.acquire()  # 上锁
    lock.acquire()  # 死锁
    # 不执行
    global num
    for i in range(10_000_000):
        num -= 1
    lock.release()
    lock.release()


if __name__ == "__main__":
    lock = threading.Lock()

    subThread01 = threading.Thread(target=add)
    subThread02 = threading.Thread(target=sub)

    subThread01.start()
    subThread02.start()

    subThread01.join()
    subThread02.join()

    print("num result : %s" % num)
with语句

由于threading.Lock()对象中实现了enter__()与__exit()方法,故我们可以使用wit

Python线程锁 Python 中,线程锁是多线程编程中保障数据一致性和正确性的关键机制。通过合理使用互斥、递归、条件变量和信号量,开发者可以有效地管理对共享资源的访问,避免数据竞争和潜在的死情形。 阅读详情

相关推荐

Python中的线程安全单例模式实现详解

然而,在多线程环境下,如果不进行适当的同步,可能会导致多个线程同时创建实例,从而破坏单例模式的初衷。除了上述两种方法外,还有其他一些实现线程安全单例模式的技巧,比如使用装饰器(decorator)来封装单例的创建过程,或者使用Python的元类(metaclass)来动态地修改类的创建行为。但是,如果需要在类的构造函数中执行一些复杂的初始化操作,或者需要更精细地控制实例的创建和销毁过程,那么使用。Python的模块在导入时是线程安全的,这意味着如果我们将单例实例保存为模块级变量,则可以自动实现线程安全。

铭渊老黄 1143

Python爬虫——Python线程爬虫详解

网络爬虫程序是一种 IO 密集型程序,程序中涉及了很多网络 IO 以及本地磁盘 IO 操作,这些都会消耗大量的时间,从而降低程序的执行效率,而 Python 提供的多线程能够在一定程度上提升 IO 密集型程序的执行效率。

Itmastergo的博客 2043

Python爬虫编程思想(139):多线程和多进程爬虫--线程锁

线程锁的目的是将一段代码住,一旦获得了权限,除非释放线程锁,否则其他任何代码都无法再次获得权限。

一个被知识诅咒的人 2373

python——线程同步和线程安全

python线程安全和线程同步

iamballer77的博客 1073

python 线程安全和

python 线程安全和

Deng872347348的博客 2661

Python网络爬虫

Python网络爬虫—给????爪巴????1.网络爬虫的安全性2.网络爬虫的工作原理3.requests库1.requests库的常用函数2.request操作步骤3.response返回响应4.beautifulsoup4库1.常用的解析技术2.beautifulsoup4的操作步骤 网络爬虫,又被称为网页蜘蛛,网络机器人,是一种按照一定的规则,自动请求万维网网站并提取网络数据的程序或脚本。 网络爬虫的相...

Aurora_1970s的博客 4554

Python实现网络爬虫

本人刚学Python不久,前段时间看到华南师范大学有一个网络爬虫的项目,特意研究了一下,用Python写了一个可以多线程下载的网络爬虫。 其实网络爬虫的原理并不是太复杂,具体的一些东西可以参照百度百科的描述,在这里我就不再多说。 我使用的网络爬虫下载网页的算法是广度搜索(BFS),网络上对爬虫实现算法的评价中,广度搜索的算法是排行第二的,最好的算法是按网页重要性排序后再确定下载顺序(这

2x 青年,欢乐多。 9236

Python实战:用多线程和多进程打造高效爬虫

通过使用多线程和多进程,我们可以显著提高网络爬虫的效率,更快地获取大量数据。然而,要小心线程安全问题和进程管理的开销。挑战与创造都是很痛苦的,但是很充实。

null18的博客 7557

打开我的收藏夹 -- Python爬虫

文章目录前言线程锁retrying模块aiohttp模块requests-html模块获取网页源码获取链接获取元素JavaScript支持!!! 前言 这两天都没更新啥哈,太忙了。 今天这篇会比较特殊一些,因为爬虫我会的也写的差不多了,但是我还有一个背后隐藏能源还没拿出来用呢!!! 今天就打开这个隐藏能源,这可是一个大佬多年呕心沥血打造的,看看我能从中学到多少东西。 线程锁 哈哈,先来就讲这个和爬虫“不太沾边”的东西,是不是不好啊。 其实是很有关系的啦。如果你要靠自己大批量爬取内容,肯定是离不开多线程..

看,未来的博客 7234

python 爬虫实现前需要注意的地方

网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁,自动索引,模拟程序或者蠕虫。 参看百度百科 http://note.sdo.com/u/wyatt/n/mbnUS~jpc4spnM00k001a5 最近

NN179的专栏 6819

Python爬虫初体验(3):线程锁线程同步与异常问题的解决

前情提要:Python爬虫初体验(2):多线程的应用及爬取中的实际问题 从来没想过,写一个功能较为完备的爬虫代码,要花好几天的时间…… 这次算是增长了许多编程经验。 好的废话不多说,进入正题 上次的代码中,由于部分XKCD漫画有特殊格式(还有 404 彩蛋),而我的代码中 try-except 结构只处理了连接超时的问题,没有对漫画本身的格式变化进行处理,导致线程意外中断。虽然最后写了重试...

江澤妮可 629

Python】多线程线程安全和

首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。每个线程在执行的时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU。

专注上位机开发、机器视觉、机器学习、嵌入式软硬件开发 1万+

5 - 线程 - Windows 10 - Python5线程锁 - 控制线程切换

目录线程安全Tips线程的切换线程锁的基础核心构成总结1. Lock() 同步线程锁 - 同步的实现方法with 关键字 - 上下文管理器 - 同步现象2. RLock() 递归简介线程锁 - 递归的实现方法with 关键字 - 上下文管理器 - 递归3. Condition() 条件条件调用 —— 必上递归的 wait() 与 notify()线程锁 - 条件的实现方法with 关键字 - 上下文管理器 - 条件4. Event() 事件基本介绍线程锁 - 事件的实现方法事

Muxiu 2940

掌握Python并发编程:线程安全的秘诀

在多线程环境中编程时,确保操作的线程安全是至关重要的,特别是当涉及到共享资源如Python中的容器类型(list、set、dict)及其他共享数据结构时。本文通过探讨Python线程模型、线程安全的基本概念,以及深入分析容器类型在多线程应用中的线程安全操作,提供了一系列的实际案例分析和代码示例,旨在帮助开发者理解和实现线程安全的多线程编程。Python线程是操作系统级别的线程,这意味着Python的多线程是由操作系统调度的,能够实现真正的并行计算,但在解释器级别受到了GIL的限制。此外,Python的。

walkskyer的博客 2265

Python线程操作之

Python线程操作之

Python热爱者的博客 905
上一篇: python爬虫之JS逆向基础小案例:网抑云数据获取
下一篇: python基础小知识:引用和赋值的区别
魔王不会哭
博客等级 码龄5年 6002粉丝 394原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值