一、进程锁
我们有时候会遇到这样的情况,当有100个线程同时去操作一个文件或者修改一个数据的时候,会发生什么呢?
我们来看一下下面的例子
from time import sleep
from threading import Thread
class Account(object):
def __init__(self):
self._balance = 0
def deposit(self, money):
# 计算存款后的余额
new_balance = self._balance + money
# 模拟受理存款业务需要0.01秒的时间
sleep(0.01)
# 修改账户余额
self._balance = new_balance
@property
def balance(self):
return self._balance
class AddMoneyThread(Thread):
def __init__(self, account, money):
super().__init__()
self._account = account
self._money = money
def run(self):
self._account.deposit(self._money)
def main():
account = Account()
threads = []
# 创建100个存款的线程向同一个账户中存钱
for _ in range(100):
t = AddMoneyThread(account, 1)
threads.append(t)
t.start()
# 等所有存款的线程都执行完毕
for t in threads:
t.join()
print('账户余额为: ¥%d元' % account.balance)
if __name__ == '__main__':
main()
我们创造了100个线程来对余额进行了加一块钱的操作,按道理我们最后的结果应该是100元,但是输出的结果竟然还是1块,如果这是发生在银行卡上的操作,那将是绝对不允许的。
产生的原因是因为这100个线程几乎是同是启动的,那么它们拿到的初始数据肯定都是0,那么加1后返还,那必定是1块了。
那我们解决这个问题的最佳方法就是,加上进程锁或者是线程锁,在一个线程对数据进行操作的时候,其余线程会堵塞,直到上一个线程执行完毕,下一个线程才能继续进行操作。虽然这会降低程序运行的效率,但在这种情况下,数据的一致性的优先级是最高的
from time import sleep
from threading import Thread, Lock
class Account(object):
def __init__(self):
self._balance = 0
self._lock = Lock()
def deposit(self, money):
# 先获取锁才能执行后续的代码
self._lock.acquire()
try:
new_balance = self._balance + money
sleep(0.01)
self._balance = new_balance
finally:
# 在finally中执行释放锁的操作保证正常异常锁都能释放
self._lock.release()
@property
def balance(self):
return self._balance
class AddMoneyThread(Thread):
def __init__(self, account, money):
super().__init__()
self._account = account
self._money = money
def run(self):
self._account.deposit(self._money)
def main():
account = Account()
threads = []
for _ in range(100):
t = AddMoneyThread(account, 1)
threads.append(t)
t.start()
for t in threads:
t.join()
print('账户余额为: ¥%d元' % account.balance)
if __name__ == '__main__':
main()
二、信号量
信号量最直观的解释同一时间一段代码代码能被多少子进程运行的数量,如果一段代码能同时被四个子进程运行,那么信号量就是4。这个信号量的实现也是基于进程锁的原理,进程锁是一把锁,而信号量的实现类似于分配若干进程锁来实现。
import random
from multiprocessing import Process,Semaphore
import time
def movie(name,sem):
sem.acquire() #获取进程锁
print("%s is watching movie"%name)
time.sleep(random.randint(1,4))
print("%s exit"%name)
sem.release()
if __name__ == '__main__':
sem = Semaphore(4) #设置四把进程锁
for i in range(8):
p = Process(target=movie,args=(i,sem))
p.start()