举例讲解Python编程中对线程锁的使用


Posted in Python onJuly 12, 2016

python的内置数据结构比如列表和字典等是线程安全的,但是简单数据类型比如整数和浮点数则不是线程安全的,要这些简单数据类型的通过操作,就需要使用锁。

#!/usr/bin/env python3
# coding=utf-8

import threading

shared_resource_with_lock = 0
shared_resource_with_no_lock = 0
COUNT = 100000
shared_resource_lock = threading.Lock()

####LOCK MANAGEMENT##
def increment_with_lock():
  global shared_resource_with_lock
  for i in range(COUNT):
    shared_resource_lock.acquire()
    shared_resource_with_lock += 1
    shared_resource_lock.release()
    
def decrement_with_lock():
  global shared_resource_with_lock
  for i in range(COUNT):
    shared_resource_lock.acquire()
    shared_resource_with_lock -= 1
    shared_resource_lock.release()
    ####NO LOCK MANAGEMENT ##
  
def increment_without_lock():
  global shared_resource_with_no_lock
  for i in range(COUNT):
    shared_resource_with_no_lock += 1
  
def decrement_without_lock():
  global shared_resource_with_no_lock
  for i in range(COUNT):
    shared_resource_with_no_lock -= 1
  
####the Main program
if __name__ == "__main__":
  t1 = threading.Thread(target = increment_with_lock)
  t2 = threading.Thread(target = decrement_with_lock)
  t3 = threading.Thread(target = increment_without_lock)
  t4 = threading.Thread(target = decrement_without_lock)
  t1.start()
  t2.start()
  t3.start()
  t4.start()
  t1.join()
  t2.join()
  t3.join()
  t4.join()
  print ("the value of shared variable with lock management is %s"\
  %shared_resource_with_lock)
  print ("the value of shared variable with race condition is %s"\
  %shared_resource_with_no_lock)

执行结果:

$ ./threading_lock.py
the value of shared variable with lock management is 0
the value of shared variable with race condition is 0

又如:

import random
import threading
import time
logging.basicConfig(level=logging.DEBUG,
          format='(%(threadName)-10s) %(message)s',
          )
          
class Counter(object):
  def __init__(self, start=0):
    self.lock = threading.Lock()
    self.value = start
  def increment(self):
    logging.debug(time.ctime(time.time()))
    logging.debug('Waiting for lock')
    self.lock.acquire()
    try:
      pause = random.randint(1,3)
      logging.debug(time.ctime(time.time()))
      logging.debug('Acquired lock')      
      self.value = self.value + 1
      logging.debug('lock {0} seconds'.format(pause))
      time.sleep(pause)
    finally:
      self.lock.release()
def worker(c):
  for i in range(2):
    pause = random.randint(1,3)
    logging.debug(time.ctime(time.time()))
    logging.debug('Sleeping %0.02f', pause)
    time.sleep(pause)
    c.increment()
  logging.debug('Done')
counter = Counter()
for i in range(2):
  t = threading.Thread(target=worker, args=(counter,))
  t.start()
logging.debug('Waiting for worker threads')
main_thread = threading.currentThread()
for t in threading.enumerate():
  if t is not main_thread:
    t.join()
logging.debug('Counter: %d', counter.value)

执行结果:

$ python threading_lock.py
(Thread-1 ) Tue Sep 15 15:49:18 2015
(Thread-1 ) Sleeping 3.00
(Thread-2 ) Tue Sep 15 15:49:18 2015
(MainThread) Waiting for worker threads
(Thread-2 ) Sleeping 2.00
(Thread-2 ) Tue Sep 15 15:49:20 2015
(Thread-2 ) Waiting for lock
(Thread-2 ) Tue Sep 15 15:49:20 2015
(Thread-2 ) Acquired lock
(Thread-2 ) lock 2 seconds
(Thread-1 ) Tue Sep 15 15:49:21 2015
(Thread-1 ) Waiting for lock
(Thread-2 ) Tue Sep 15 15:49:22 2015
(Thread-1 ) Tue Sep 15 15:49:22 2015
(Thread-2 ) Sleeping 2.00
(Thread-1 ) Acquired lock
(Thread-1 ) lock 1 seconds
(Thread-1 ) Tue Sep 15 15:49:23 2015
(Thread-1 ) Sleeping 2.00
(Thread-2 ) Tue Sep 15 15:49:24 2015
(Thread-2 ) Waiting for lock
(Thread-2 ) Tue Sep 15 15:49:24 2015
(Thread-2 ) Acquired lock
(Thread-2 ) lock 1 seconds
(Thread-1 ) Tue Sep 15 15:49:25 2015
(Thread-1 ) Waiting for lock
(Thread-1 ) Tue Sep 15 15:49:25 2015
(Thread-1 ) Acquired lock
(Thread-1 ) lock 2 seconds
(Thread-2 ) Done
(Thread-1 ) Done
(MainThread) Counter: 4

acquire()中传入False值,可以检查是否获得了锁。比如:

import logging
import threading
import time
logging.basicConfig(level=logging.DEBUG,
          format='(%(threadName)-10s) %(message)s',
          )
          
def lock_holder(lock):
  logging.debug('Starting')
  while True:
    lock.acquire()
    try:
      logging.debug('Holding')
      time.sleep(0.5)
    finally:
      logging.debug('Not holding')
      lock.release()
    time.sleep(0.5)
  return
          
def worker(lock):
  logging.debug('Starting')
  num_tries = 0
  num_acquires = 0
  while num_acquires < 3:
    time.sleep(0.5)
    logging.debug('Trying to acquire')
    have_it = lock.acquire(0)
    try:
      num_tries += 1
      if have_it:
        logging.debug('Iteration %d: Acquired',
               num_tries)
        num_acquires += 1
      else:
        logging.debug('Iteration %d: Not acquired',
               num_tries)
    finally:
      if have_it:
        lock.release()
  logging.debug('Done after %d iterations', num_tries)
lock = threading.Lock()
holder = threading.Thread(target=lock_holder,
             args=(lock,),
             name='LockHolder')
holder.setDaemon(True)
holder.start()
worker = threading.Thread(target=worker,
             args=(lock,),
             name='Worker')
worker.start()

执行结果:

$ python threading_lock_noblock.py
(LockHolder) Starting
(LockHolder) Holding
(Worker  ) Starting
(LockHolder) Not holding
(Worker  ) Trying to acquire
(Worker  ) Iteration 1: Acquired
(LockHolder) Holding
(Worker  ) Trying to acquire
(Worker  ) Iteration 2: Not acquired
(LockHolder) Not holding
(Worker  ) Trying to acquire
(Worker  ) Iteration 3: Acquired
(LockHolder) Holding
(Worker  ) Trying to acquire
(Worker  ) Iteration 4: Not acquired
(LockHolder) Not holding
(Worker  ) Trying to acquire
(Worker  ) Iteration 5: Acquired
(Worker  ) Done after 5 iterations

线程安全锁

threading.RLock()

返回可重入锁对象。重入锁必须由获得它的线程释放。一旦线程获得了重入锁,同一线程可不阻塞地再次获得,获取之后必须释放。

通常一个线程只能获取一次锁:

import threading

lock = threading.Lock()

print 'First try :', lock.acquire()
print 'Second try:', lock.acquire(0)

执行结果:

$ python threading_lock_reacquire.py
First try : True
Second try: False

使用RLock可以获取多次锁:

import threading
lock = threading.RLock()
print 'First try :', lock.acquire()
print 'Second try:', lock.acquire(0)

执行结果:

python threading_rlock.py
First try : True
Second try: 1

再来看一个例子:

#!/usr/bin/env python3
# coding=utf-8
import threading
import time
class Box(object):
  lock = threading.RLock()
  def __init__(self):
    self.total_items = 0
  def execute(self,n):
    Box.lock.acquire()
    self.total_items += n
    Box.lock.release()
  def add(self):
    Box.lock.acquire()
    self.execute(1)
    Box.lock.release()
  def remove(self):
    Box.lock.acquire()
    self.execute(-1)
    Box.lock.release()
    
## These two functions run n in separate
## threads and call the Box's methods    
def adder(box,items):
  while items > 0:
    print ("adding 1 item in the box\n")
    box.add()
    time.sleep(5)
    items -= 1
    
def remover(box,items):
  while items > 0:
    print ("removing 1 item in the box")
    box.remove()
    time.sleep(5)
    items -= 1
    
## the main program build some
## threads and make sure it works
if __name__ == "__main__":
  items = 5
  print ("putting %s items in the box " % items)
  box = Box()
  t1 = threading.Thread(target=adder,args=(box,items))
  t2 = threading.Thread(target=remover,args=(box,items))
  t1.start()
  t2.start()
  t1.join()
  t2.join()
  print ("%s items still remain in the box " % box.total_items)

执行结果:

$ python3 threading_rlock2.py
putting 5 items in the box 
adding 1 item in the box
removing 1 item in the box
adding 1 item in the box
removing 1 item in the box
adding 1 item in the box
removing 1 item in the box
removing 1 item in the box
adding 1 item in the box
removing 1 item in the box
adding 1 item in the box
0 items still remain in the box
Python 相关文章推荐
对于Python异常处理慎用“except:pass”建议
Apr 02 Python
初步解析Python中的yield函数的用法
Apr 03 Python
Python中使用PyQt把网页转换成PDF操作代码实例
Apr 23 Python
Python排序算法实例代码
Aug 10 Python
Python抓取聚划算商品分析页面获取商品信息并以XML格式保存到本地
Feb 23 Python
python 对dataframe下面的值进行大规模赋值方法
Jun 09 Python
pandas 读取各种格式文件的方法
Jun 22 Python
python字符串切割:str.split()与re.split()的对比分析
Jul 16 Python
PyQt5+Pycharm安装和配置图文教程详解
Mar 24 Python
python3读取autocad图形文件.py实例
Jun 05 Python
python实现PDF中表格转化为Excel的方法
Jun 16 Python
Python基于opencv的简单图像轮廓形状识别(全网最简单最少代码)
Jan 28 Python
使用Python编写一个最基础的代码解释器的要点解析
Jul 12 #Python
Python中使用bidict模块双向字典结构的奇技淫巧
Jul 12 #Python
Python使用SocketServer模块编写基本服务器程序的教程
Jul 12 #Python
使用Python的Flask框架表单插件Flask-WTF实现Web登录验证
Jul 12 #Python
Python的Flask框架标配模板引擎Jinja2的使用教程
Jul 12 #Python
深度定制Python的Flask框架开发环境的一些技巧总结
Jul 12 #Python
Python的面向对象编程方式学习笔记
Jul 12 #Python
You might like
PHP基础陷阱题(变量赋值)
2012/09/12 PHP
php可应用于面包屑导航的迭代寻找家谱树实现方法
2015/02/02 PHP
Laravel 5 框架入门(一)
2015/04/09 PHP
php封装的连接Mysql类及用法分析
2015/12/10 PHP
php利用header函数下载各种文件
2016/08/24 PHP
CL vs ForZe BO5 第四场 2.13
2021/03/10 DOTA
javascript轻量级模板引擎juicer使用指南
2014/06/22 Javascript
Vuejs第十篇之vuejs父子组件通信
2016/09/06 Javascript
vue中axios处理http发送请求的示例(Post和get)
2017/10/13 Javascript
vue中的模态对话框组件实现过程
2018/05/01 Javascript
angularJs中$scope数据序列化的实例
2018/09/30 Javascript
javascript中一些奇葩的日期换算方法总结
2018/11/14 Javascript
移动端(微信等使用vConsole调试console的方法
2019/03/05 Javascript
详解js获取video任意时间的画面截图
2019/04/17 Javascript
vue实现滑动到底部加载更多效果
2020/10/27 Javascript
vue.js实现只能输入数字的输入框
2019/10/19 Javascript
Vue之封装公用变量以及实现方式
2020/07/31 Javascript
[05:15]DOTA2英雄梦之声_第16期_灰烬之灵
2014/06/21 DOTA
[01:35]2018年度CS GO最佳战队-完美盛典
2018/12/17 DOTA
Python脚本简单实现打开默认浏览器登录人人和打开QQ的方法
2016/04/12 Python
python append、extend与insert的区别
2016/10/13 Python
基于Python中单例模式的几种实现方式及优化详解
2018/01/09 Python
python+matplotlib绘制旋转椭圆实例代码
2018/01/12 Python
python基础教程项目五之虚拟茶话会
2018/04/02 Python
python实现输出一个序列的所有子序列示例
2019/11/18 Python
Python for循环搭配else常见问题解决
2020/02/11 Python
matplotlib绘制鼠标的十字光标的实现(自定义方式,官方实例)
2021/01/10 Python
HTML5 Video标签的属性、方法和事件汇总介绍
2015/04/24 HTML / CSS
印度尼西亚电子产品购物网站:Kliknklik
2018/06/05 全球购物
大学生大二自我鉴定
2013/10/28 职场文书
应届生学校辅导员求职信
2013/11/07 职场文书
企业管理部经理岗位职责
2013/12/24 职场文书
房地产销售计划书
2014/01/10 职场文书
毕业自我评价
2014/02/05 职场文书
中华魂放飞梦想演讲稿
2014/08/26 职场文书
护士2014年终工作总结
2014/11/11 职场文书