Python中级教程:深入理解临界区加锁概念及其在中小企业网站建设中的应用

Python中级精华-临界区加锁

Python中级教程:深入理解临界区加锁概念及其在中小企业网站建设中的应用

概念梳理:

专注于为中小企业提供服务,包括成都网站设计和成都网站制作。实现电脑端、手机端、微信端的三站合一,能进行更高效的管理。我们立足成都,凝聚了互联网行业人才,推动了成百上千家企业稳健成长,帮助中小企业通过网站建设实现规模扩充与转变,还为中小企业免费做网站,提供优质服务。

临界区:

临界区指的是一个程序片段,这个程序片段用于访问共用资源,比如共用设备或者共用存储器。

这些共用资源具有无法同时被多个线程访问的特性。当某一线程进入临界区段时,其他线程或进程就必须等待。为了确保这些共用资源能被互斥地使用,一些同步机制必须在临界区段的进入点和离开点得以实现。

目的:

我们在多线程

处理时常会涉及对一块共享资源的访问,在此情况下,需要线程能够互斥,以避免出现竞态条件。

方法:

要让可变对象能在多线程环境中安全访问,需使用锁变量,即 threading 中的 LockRLock,锁变量属于同步原语。

中的最常见一种之一。

示例:

线程的调度本质上是非确定性的。它只能保证对资源有独一访问,但无法保证各个线程的执行先后顺序。只要共享的可变状态需要被多个线程访问,就必须使用锁机制,以此来保证数据的安全。

在 threading 库中,我们还发现了其他的同步原语。比如有 RLock(可重入锁),还有 Semaphore 对象(信号量)。

RLock 能够被同一个线程多次进行获取操作。它主要的用途是用于编写基于锁的代码,同时也可以用于基于“监听器”的同步处理。

某个类持有这种类型锁时,仅有一个线程能够使用类里的全部函数或者方法。比如:

这份代码仅有一个锁,其作用于整个类,所有类实例都共享此锁。此锁不再绑定在某个实例的可变状态上,如今是用于同步类中的方法的。与其他标准锁不同的是,若已持有该锁的方法,可调用同样使用此锁的其他方法(参考 sub_one())。

这个实现的特点在于,创建了很多 counter 实例的情况下,这些实例拥有同一把锁。所以,当有众多 counter 出现时,这种方法在堆内存的使用效率方面会高许多。然而,可能存在的缺点是,在使用了大量线程并且需要频繁对 counter 中的数据进行更新时,这样做会引发锁争用的情况。

另有一种同步原语叫 semaphore,它是基于共享计数器的同步原语。若计数器不为 0,with 语句会使计数器递减且允许线程继续执行。with 语句块结束后,会将计数器递增。若计数器为 0,执行过程会被阻塞,直至有另一个线程来递增计数器。因为信号量

实现起来更为复杂,这会给程序带来一定的不良影响。除了具备简单的加锁功能之外,信号量对象在那些设计用于在线程间发送信号或者需要实现节流处理的应用中更为实用,比如限制并发的总数:

python多线程几种方法实现

Python进阶(二十六)-多线程实现同步的四种方式

临界资源指的是一次只能被一个线程访问的资源。例如打印机,它一次只能被一个程序用于执行打印功能,因为不能多个线程同时对其进行操作。而访问这部分资源的代码通常被称作临界区。

锁机制

Lock 类用于 threading 操作,使用该类的 acquire 函数可实现加锁,使用 release 函数可实现解锁。

定义一个名为 Num 的类。

def __init__(self):

self.num = 0

定义了一个名为 add 的方法。

加锁,锁住相应的资源,通过 self.lock.acquire() 实现

self.num += 1

num = self.num

self.lock.release()#解锁,离开该资源

return num

n 等于 Num()。类 jdThread 是 threading.Thread 的子类。

def __init__(self,item):

创建一个新的线程对象,初始化该线程对象的自身部分。

这个类有一个 run 方法 。

time.sleep(2)

先将 num 加 1,然后输出原来的数据和加 1 之后的数据。

对于在范围(5)中的 item:

t = jdThread(item)

t.start()

t 执行 join()方法,这样能让线程按照顺序一个一个地执行,分别执行 12345678910111213141516171819202122232425262728 这些内容。

一个线程调用锁的 acquire()方法后能获得锁,此时锁进入“locked”状态。每次只有一个线程能够获得锁。若此时有另一个线程试图获取这个锁,那么这个线程就会变成“blocked”状态,这被称为“同步阻塞”(可参考多线程的基本概念)。

锁在拥有它的线程调用 release()方法释放锁之后,会进入“unlocked”状态。线程调度程序会从处于同步阻塞状态的线程里挑选一个来获取锁,并且让这个线程进入运行(running)状态。

信号量

信号量提供 acquire 方法和 release 方法。每当调用 acquire 方法时,若内部计数器大于 0,就将其减 1;若内部计数器等于 0,就会阻塞该线程,直至有线程调用 release 方法将内部计数器更新到大于 1 的位置。

import threadingimport timeclass Num:

def __init__(self):

self.num = 0

self.sem 是一个信号量对象,其初始值被设置为 3,这意味着最多允许三个线程同时访问资源。

def add(self):

self.sem.acquire()#内部计数器减1

self.num += 1

num = self.num

self.sem.release()#内部计数器加1

return num

n = Num()class jdThread(threading.Thread):

def __init__(self,item):

threading.Thread.__init__(self)

self.item = item def run(self):

time.sleep(2)

value = n.add()

对于在 100 范围内的每一个 item,打印出 self.item 和 value

python多线程全局变量和锁

python 中的数据类型包括 int、float、复数、字符、元组等。当这些数据类型作为全局变量使用时,需要在函数内部使用 global 来声明变量,这样才能对变量进行操作。

而,对象,列表,词典,不需要声明,直接就是全局的。

2.线程锁mutex=threading.Lock()

创建后就是全局的。线程调用函数可以直接在函数中使用。

mutex.acquire()开启锁

mutex=release()关闭锁

要注意,死锁的情况发生。

注意运行效率的变化:

正常1秒,完成56997921

加锁之后,1秒只运行了531187,相差10倍多。

继承 threading.Thread 的类,不能够调用__init__函数,也不能够在创建对象的时候对新建的属性进行初始化。

4.线程在cpu的执行,有随机性

新建线程时,若需要传参数,args 为一个元组。当只有一个参数时,必须在后面加上一个逗号符号。若只有一个参数,线程将会报创建参数错误。例如 threading.Thread(target=fuc,args=(arg,)) 。

Python中的各种锁?

大致罗列一下:

一、全局解释器锁(GIL)

1、什么是全局解释器锁

每个 CPU 在同一时间仅能执行一个线程。这样一来,其他的线程就必须等待该线程的全局解释器。只有当该线程的全局解释器使用权消失后,其他线程才能使用全局解释器。即便多个线程在同一个进程下不会相互影响,但也只有一个线程能够使用 CPU。这种机制被称为全局解释器锁(GIL)。GIL 的设计对 CPython 的实现进行了简化。对象模型包含关键的内建类型,例如字典等,这些类型是隐含的,并且可以并发访问。锁住全局解释器使得能够比较容易地实现对多线程的支持,但同时也损失了多处理器主机的并行计算能力。

2、全局解释器锁的好处

1)、避免了大量的加锁解锁的好处

2)、使数据更加安全,解决多线程间的数据完整性和状态同步

3、全局解释器的缺点

多核处理器退化成单核处理器,只能并发不能并行。

4、GIL的作用:

多线程情况下必然存在资源的竞争。GIL 的作用是确保在解释器这个级别上,线程能够唯一地使用共享资源,也就是 cpu。

二、同步锁

1、什么是同步锁?

一个进程下的一个线程在同一时刻只能使用一个 cpu。要让这个线程下的程序在一段时间内被 cpu 执行,就需要用到同步锁。

2、为什么用同步锁?

当一个线程在使用 cpu 时,该线程下的程序可能会遇到 io 操作。如果遇到 io 操作,cpu 就会切到别的线程上去。这样就有可能影响到该程序结果的完整性。

3、怎么使用同步锁?

只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。

4、同步锁的所用:

为了保证在解释器级别下,自己编写的程序能唯一使用共享资源,所以产生了同步锁。

三、死锁

1、什么是死锁?

两个或两个以上的线程或进程在执行程序时,会因争夺资源或者程序推进顺序不当而出现相互等待的情况,这就是一个现象。 或者 两个或两个以上的线程或进程在执行程序过程中,由于争夺资源或者程序推进顺序不当,就会出现相互等待的现象。

2、死锁产生的必要条件?

互斥条件、请求和保持条件、不剥夺条件、环路等待条件

3、处理死锁的基本方法?

预防死锁,要采取相应措施;避免死锁,可运用银行家算法;检测死锁,需从资源分配方面着手;解除死锁,有剥夺资源和撤销进程这两种方式。

四、递归锁

Python为支持同一个线程中多次请求同一资源,提供了可重入锁。该 RLock 内部会维护一个 Lock 以及一个 counter 变量,counter 用于记录 acquire 的次数,这样就能让资源被多次 require 。只有当一个线程的所有 acquire 都被 release 后,其他线程才可以获得资源。递归锁分为可递归锁与非递归锁。

五、乐观锁

假设不会出现并发冲突,仅在进行提交操作的时候去检查是否违背了数据完整性。

六、悲观锁

假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

python 常用的加锁方式有互斥锁;还有可重入锁;以及迭代死锁;包括互相调用死锁;另外还有自旋锁,现大致罗列如下:

一、全局解释器锁(GIL)

1、什么是全局解释器锁

每个CPU在同一时间只能执行一个线程,那么其他的线程就必须等待该线程的全局解释器,使用权消失后才能使用全局解释器,即使多个线程直接不会相互影响在同一个进程下也只有一个线程使用cpu,这样的机制称为全局解释器锁(GIL)。GIL的设计简化了CPython的实现,使的对象模型包括关键的内建类型,如:字典等,都是隐含的,可以并发访问的,锁住全局解释器使得比较容易的实现对多线程的支持,但也损失了多处理器主机的并行计算能力。

2、全局解释器锁的好处

1)、避免了大量的加锁解锁的好处

2)、使数据更加安全,解决多线程间的数据完整性和状态同步

3、全局解释器的缺点

多核处理器退化成单核处理器,只能并发不能并行。

4、GIL的作用:

多线程情况下必须存在资源的竞争,GIL是为了保证在解释器级别的线程唯一使用共享资源(cpu)。

二、同步锁

1、什么是同步锁?

同一时刻的一个进程下的一个线程只能使用一个cpu,要确保这个线程下的程序在一段时间内被cpu执,那么就要用到同步锁。

2、为什么用同步锁?

因为有可能当一个线程在使用cpu时,该线程下的程序可能会遇到io操作,那么cpu就会切到别的线程上去,这样就有可能会影响到该程序结果的完整性。

3、怎么使用同步锁?

只需要在对公共数据的操作前后加上上锁和释放锁的操作即可。

4、同步锁的所用:

为了保证解释器级别下的自己编写的程序唯一使用共享资源产生了同步锁。

三、死锁

1、什么是死锁?

指两个或两个以上的线程或进程在执行程序的过程中,因争夺资源或者程序推进顺序不当而相互等待的一个现象。

2、死锁产生的必要条件?

互斥条件、请求和保持条件、不剥夺条件、环路等待条件

3、处理死锁的基本方法?

预防死锁、避免死锁(银行家算法)、检测死锁(资源分配)、解除死锁:剥夺资源、撤销进程

四、递归锁

在Python中为了支持同一个线程中多次请求同一资源,Python提供了可重入锁。这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。递归锁分为可递归锁与非递归锁。

五、乐观锁

假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

六、悲观锁

假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

python 常用的加锁方式有:互斥锁;可重入锁;迭代死锁;互相调用死锁;自旋锁。

新闻名称:python函数加锁 python程序锁

文章源于:

成都有一家网站建设公司叫创新互联,它能为您提供自适应网站,还能提供服务器托管服务,同时也能制作微信小程序,并且可以帮您进行域名注册,另外还涉及 GPT 以及网站设计等业务。

广告

你可能想看:
« 上一篇
下一篇 »

相关推荐

python爬虫教程PDF,Python爬虫书PDF

2026年09月02日

31阅读

外国教程python,python编程教学视频

2026年09月02日

25阅读

python简明教程沈洁元,python详细基础教程

2026年09月02日

21阅读

python库包教程,python 包

2026年09月02日

26阅读

python教程大全集,python教程600集

2026年09月02日

26阅读

python入门教程完整版,python零基础入门全套教程视频

2026年09月02日

22阅读