Python中级精华-临界区加锁

Python017

Python中级精华-临界区加锁,第1张

概念梳理:

临界区:

临界区指的是一个访问共用资源(例如:共用设备或是共用存储器)的程序片段

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

目的:

我们在多线程

处理是,经常会涉及到对于一块共享资源的访问,这里就需要线程可以互斥去避免出现竞态条件(race condition).

方法:

要想让可变对象安全地在多线程环境中进行访问,就要使用锁变量(threading中的LockRLock),锁变量也是同步原语

中的最常见一种之一。

示例:

线程的调度从本质上来说是非确定性的(只能保证独一访问,但保证不了谁先谁后)。只要共享的可变状态需要被多个线程访问,就要使用锁机制,保证数据的安全。

在threading库中我们也发现了其他的同步原语例如:RLock(可重入锁)、Semaphore对象(信号量)。

RLock:可以被同一个线程多次获取,主要用来编写基于锁的代码,或者基于‘监听器’的同步处理。

当某个类持有这种类型锁时,只有一个线程可以使用类中全部函数或者方法,例如:

这份代码中只有一个作用于整个类的锁,它被所有的类实例所共享,不再将所绑定在某个实例的可变状态上,现在这个锁是用来同步类中的方法的。对于其他标准锁不同的是,对于已经持有了该锁的方法可以调用同样使用了这个锁的其他方法(参考sub_one())。

这个实现的特点是,无论创建了多少counter实例,这些实例共有同一把锁。因此,当有大量counter出现时,这种方法堆内存的使用效率要高很多。但是可能存在的缺点是在使用了大量线程且需要频繁更新counter中的数据时,这么做会出现锁争用的情况。

另外一种同步原语semaphore,是一种基于共享计数器的同步原语。如果计数器非0,那么with语句会递减计数器并且允许线程继续执行。当with语句块结束后,会将计数器递增。如果计数器为0,那么执行过程会被阻塞,直到由另外一个线程来递增计数器为止。由于信号量

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

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

临界资源即那些一次只能被一个线程访问的资源,典型例子就是打印机,它一次只能被一个程序用来执行打印功能,因为不能多个线程同时操作,而访问这部分资源的代码通常称之为临界区。

锁机制

threading的Lock类,用该类的acquire函数进行加锁,用realease函数进行解锁

import threadingimport timeclass Num:

def __init__(self):

self.num = 0

self.lock = threading.Lock()def add(self):

self.lock.acquire()#加锁,锁住相应的资源

self.num += 1

num = self.num

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

return num

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

def __init__(self,item):

threading.Thread.__init__(self)

self.item = itemdef run(self):

time.sleep(2)

value = n.add()#将num加1,并输出原来的数据和+1之后的数据

print(self.item,value)for item in range(5):

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 = threading.Semaphore(value = 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 = itemdef run(self):

time.sleep(2)

value = n.add()

print(self.item,value)for item in range(100):