线程同步技术:

解决多个线程争抢同一个资源的情况,线程协作工作。一份数据同一时刻只能有一个线程处理。

解决线程同步的几种方法:

Lock、RLock、Condition、Barrier、semaphore

1)Lock 锁

锁,一旦线程获得锁,其它试图获取锁的线程将被阻塞。

当用阻塞参数设置为 False 时, 不要阻止。如果将阻塞设置为 True 的调用将阻止, 则立即返回 False;否则, 将锁定设置为锁定并返回 True。

Lock的方法:
acquire(blocking=True,timeout=-1)  加锁。默认True阻塞,阻塞可以设置超时时间。非阻塞时成功获取锁返回True,否则返回False。

当blocking设置为False时,不阻塞,同一个锁对象,其它线程可以重用,但最后都必须释放。

如果设置为True(默认True),其它试图调用锁的线程将阻塞,并立即返回False。阻塞可以设置超时时间。

release() 释放锁。可以从任何线程调用释放。已上锁的锁,会被重置为unlocked,对未上锁的锁调用,会抛RuntimeError异常: cannot release un-acquired lock。

不使用Lock的例子:

  1. #不使用Lock锁的例子
  2. import logging
  3. import threading,time
  4. logging.basicConfig(level=logging.INFO)
  5.  
  6. # 10 -> 100cups
  7. cups = []
  8. lock = threading.Lock()
  9.  
  10. def worker(lock:threading.Lock,task=100):
  11. while True:
  12. count = len(cups)
  13. time.sleep(0.1)
  14.  
  15. if count >= task:
  16. break
  17.  
  18. logging.info(count)
  19. cups.append(1)
  20. logging.info("{} make 1........ ".format(threading.current_thread().name))
  21. logging.info("{} ending=======".format(len(cups)))
  22.  
  23. for x in range(10):
  24. threading.Thread(target=worker,args=(lock,100)).start()
  25.  
  26. 运行结果:
  27. INFO:root:Thread-7 make 1........
  28. INFO:root:93
  29. INFO:root:Thread-5 make 1........
  30. INFO:root:95
  31. INFO:root:Thread-6 make 1........
  32. INFO:root:92
  33. INFO:root:Thread-2 make 1........
  34. INFO:root:94
  35. INFO:root:Thread-8 make 1........
  36. INFO:root:97
  37. INFO:root:Thread-10 make 1........
  38. INFO:root:96
  39. INFO:root:Thread-4 make 1........
  40. INFO:root:98
  41. INFO:root:Thread-1 make 1........
  42. INFO:root:99
  43. INFO:root:Thread-9 make 1........
  44. INFO:root:109 ending=======
  45. INFO:root:109 ending=======
  46. INFO:root:109 ending=======

  还是使用前面的10个工人生产100杯子的例子, 当做到99个杯子时,10个工人都发现还少一个,都去做了一个,一共做了109个,超出了100个,就发生了不可预期的结果。

临界线判断失误,多生产了杯子。

解决方法就可以用锁,来解决资源争抢。当一个人看杯子数量时,就上锁,其它人只能等着,看完杯子后发现少一个就把这最后一个做出来,然后数量加一,解锁,其他人再看到已经有100个杯子时,就可以停止工作。

加锁的时机非常重要:看杯子数量时加锁,增加数量后释放锁。

  

使用Lock的例子:

  1. #Lock
  2. import logging
  3. import threading
  4. import time
  5. logging.basicConfig(level=logging.INFO)
  6.  
  7. # 10 -> 100cups
  8. cups = []
  9. lock = threading.Lock()
  10.  
  11. def worker(lock:threading.Lock,task=100):
  12. while True:
  13. if lock.acquire(False):
  14. count = len(cups)
  15.  
  16. time.sleep(0.1)
  17.  
  18. if count >= task:
  19. lock.release()
  20. break
  21. logging.info(count)
  22.  
  23. cups.append(1)
  24. lock.release()
  25. logging.info("{} make 1........ ".format(threading.current_thread().name))
  26. logging.info("{} ending=======".format(len(cups)))
  27.  
  28. for x in range(10):
  29. threading.Thread(target=worker,args=(lock,100)).start()
  30.  
  31. 运行结果:
  32. INFO:root:0
  33. INFO:root:Thread-1 make 1........
  34. INFO:root:1
  35. INFO:root:Thread-5 make 1........
  36. INFO:root:2
  37. INFO:root:Thread-6 make 1........
  38. ....
  39. INFO:root:Thread-3 make 1........
  40. INFO:root:97
  41. INFO:root:Thread-3 make 1........
  42. INFO:root:98
  43. INFO:root:Thread-4 make 1........
  44. INFO:root:99
  45. INFO:root:Thread-3 make 1........
  46. INFO:root:100 ending=======
  47. INFO:root:100 ending=======
  48. INFO:root:100 ending=======
  49. .....

  在使用了锁以后,虽然保证了结果的准确性,但是性能下降了很多。

一般来说加锁以后还要有一些功能实现,在释放之前还有可能抛异常,一旦抛出异常,锁是无法释放,但是当前线程可能因为这个异常被终止了,这就产生了死锁。

死锁解决办法:

1、使用 try..except..finally 语句处理异常、保证锁的释放

2、with 语句上下文管理,锁对象支持上下文管理。只要实现了__enter__和__exit__魔术方法的对象都支持上下文管理。

锁的应用场景:

独占锁: 锁适用于访问和修改同一个共享资源的时候,即读写同一个资源的时候。

共享锁: 如果共享资源是不可变的值时,所有线程每一次读取它都是同一样的值,这样的情况就不需要锁。

使用锁的注意事项:

  • 少用锁,必要时用锁。使用了锁,多线程访问被锁的资源时,就变成了串行,要么排队执行,要么争抢执行。
  • 加锁时间越短越好,不需要就立即释放锁。
  • 一定要避免死锁。

不使用锁时,有了效率,但是结果是错的。

使用了锁,变成了串行,效率地下,但是结果是对的。

  1. import threading
  2. import time
  3.  
  4. lock = threading.Lock()
  5.  
  6. def work():
  7. print('working..')
  8. time.sleep(0.2)
  9. lock.release() # 1解锁
  10.  
  11. lock.acquire() # 1上锁
  12. print("get locker 1")
  13.  
  14. threading.Thread(target=work).start()
  15.  
  16. time.sleep(1)
  17. lock.acquire() # 2上锁
  18.  
  19. print("get locker 2")
  20. threading.Thread(target=work).start()
  21.  
  22. print("release locker")
  23.  
  24. 运行结果:
  25. get locker 1
  26. working..
  27. get locker 2
  28. working..
  29. release locker

  同一个锁对象在释放后可以再次使用。

但是如果同一把锁加锁后,又被别人拿了,自己就阻塞了:

  1. import threading
  2. import time
  3.  
  4. lock = threading.Lock()
  5.  
  6. def work():
  7. print('working..')
  8. time.sleep(0.2)
  9. lock.release() # 1解锁
  10.  
  11. lock.acquire() # 1上锁
  12. print("get locker 1")
  13. lock.acquire() # 2上锁
  14. print("get locker 2")
  15.  
  16. threading.Thread(target=work).start()
  17. threading.Thread(target=work).start()
  18.  
  19. print("release locker")
  20.  
  21. 运行结果:
  22. get locker 1
  23. 阻塞状态....

  

阻塞锁:

  1. #阻塞锁
  2. import threading,time
  3.  
  4. lock = threading.Lock()
  5.  
  6. def foo():
  7. ret = lock.acquire()
  8. print("{} Locked. {}".format(ret,threading.current_thread()))
  9. time.sleep(10)
  10.  
  11. threading.Thread(target=foo).start()
  12. threading.Thread(target=foo).start()
  13.  
  14. 运行结果:
  15. True Locked. <Thread(Thread-1, started 123145559191552)>

  lock.acquire()默认设置blocking=True,两个线程使用同一个Lock锁对象,只要Thread-1线程不释放,第二个线程就无法获取锁,且会使Thread-1线程阻塞。

如果想让多个线程同时都可以使用一个锁对象,就必须使用非阻塞锁,或者第一个线程使用完锁之后立刻释放,然后第二个线程再使用。

非阻塞锁:

  1. #非阻塞锁
  2. import threading,time
  3.  
  4. lock = threading.Lock()
  5.  
  6. def foo():
  7. ret = lock.acquire(False)
  8. print("{} Locked. {}".format(ret,threading.current_thread()))
  9. time.sleep(10)
  10.  
  11. threading.Thread(target=foo).start()
  12. threading.Thread(target=foo).start()
  13.  
  14. 运行结果:
  15. True Locked. <Thread(Thread-1, started 123145516146688)>
  16. False Locked. <Thread(Thread-2, started 123145521401856)>
  17.  
  18. Process finished with exit code 0

  lock.acquire(False)设置blocking=False表示不阻塞,使用同一个Lock锁对象时,第二个线程仍可以使用锁,且第一个锁不会被阻塞。

非阻塞锁2:

  1. #非阻塞锁
  2. import threading,logging,time
  3.  
  4. FORMAT = '%(asctime)s\t [%(threadName)s,%(thread)d] %(message)s'
  5. logging.basicConfig(level=logging.INFO,format=FORMAT)
  6.  
  7. def worker(tasks):
  8. for task in tasks:
  9. time.sleep(0.01)
  10. if task.lock.acquire(False): #False非阻塞
  11. logging.info('{} {} begin to start'.format(threading.current_thread().name,task.name))
  12. else:
  13. logging.info('{} {} is working'.format(threading.current_thread().name,task.name))
  14.  
  15. class Task:
  16. def __init__(self,name):
  17. self.name = name
  18. self.lock = threading.Lock()
  19.  
  20. tasks = [Task('task={}'.format(t)) for t in range(5)]
  21.  
  22. for i in range(3):
  23. t = threading.Thread(target=worker,name='worker-{}'.format(i),args=(tasks,))
  24. t.start()
  25.  
  26. 运行结果:
  27. 2017-12-19 16:37:49,556 [worker-2,123145390018560] worker-2 task=0 begin to start
  28. 2017-12-19 16:37:49,556 [worker-1,123145384763392] worker-1 task=0 is working
  29. 2017-12-19 16:37:49,557 [worker-0,123145379508224] worker-0 task=0 is working
  30. 2017-12-19 16:37:49,567 [worker-2,123145390018560] worker-2 task=1 begin to start
  31. 2017-12-19 16:37:49,567 [worker-1,123145384763392] worker-1 task=1 is working
  32. 2017-12-19 16:37:49,568 [worker-0,123145379508224] worker-0 task=1 is working
  33. 2017-12-19 16:37:49,580 [worker-1,123145384763392] worker-1 task=2 begin to start
  34. 2017-12-19 16:37:49,580 [worker-2,123145390018560] worker-2 task=2 is working
  35. 2017-12-19 16:37:49,580 [worker-0,123145379508224] worker-0 task=2 is working
  36. 2017-12-19 16:37:49,591 [worker-1,123145384763392] worker-1 task=3 begin to start
  37. 2017-12-19 16:37:49,592 [worker-2,123145390018560] worker-2 task=3 is working
  38. 2017-12-19 16:37:49,592 [worker-0,123145379508224] worker-0 task=3 is working
  39. 2017-12-19 16:37:49,604 [worker-1,123145384763392] worker-1 task=4 begin to start
  40. 2017-12-19 16:37:49,604 [worker-2,123145390018560] worker-2 task=4 is working
  41. 2017-12-19 16:37:49,604 [worker-0,123145379508224] worker-0 task=4 is working

  

[Python 多线程] Lock、阻塞锁、非阻塞锁 (八)的更多相关文章

  1. GIL全局解释器锁,线程池与进程池 同步异步,阻塞与非阻塞,异步回调

    GIL全局解释器锁 1.什么是GIL 官方解释:'''In CPython, the global interpreter lock, or GIL, is a mutex that prevents ...

  2. python并发编程(并发与并行,同步和异步,阻塞与非阻塞)

    最近在学python的网络编程,学了socket通信,并利用socket实现了一个具有用户验证功能,可以上传下载文件.可以实现命令行功能,创建和删除文件夹,可以实现的断点续传等功能的FTP服务器.但在 ...

  3. 4月27日 python学习总结 GIL、进程池、线程池、同步、异步、阻塞、非阻塞

    一.GIL:全局解释器锁 1 .GIL:全局解释器锁 GIL本质就是一把互斥锁,是夹在解释器身上的, 同一个进程内的所有线程都需要先抢到GIL锁,才能执行解释器代码 2.GIL的优缺点: 优点:  保 ...

  4. 【python】-- 事件驱动介绍、阻塞IO, 非阻塞IO, 同步IO,异步IO介绍

    事件驱动介绍 一.前言 通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收 ...

  5. python 全栈开发,Day44(IO模型介绍,阻塞IO,非阻塞IO,多路复用IO,异步IO,IO模型比较分析,selectors模块,垃圾回收机制)

    昨日内容回顾 协程实际上是一个线程,执行了多个任务,遇到IO就切换 切换,可以使用yield,greenlet 遇到IO gevent: 检测到IO,能够使用greenlet实现自动切换,规避了IO阻 ...

  6. python网络编程基础(线程与进程、并行与并发、同步与异步、阻塞与非阻塞、CPU密集型与IO密集型)

    python网络编程基础(线程与进程.并行与并发.同步与异步.阻塞与非阻塞.CPU密集型与IO密集型) 目录 线程与进程 并行与并发 同步与异步 阻塞与非阻塞 CPU密集型与IO密集型 线程与进程 进 ...

  7. Python web框架 Tornado异步非阻塞

    Python web框架 Tornado异步非阻塞   异步非阻塞 阻塞式:(适用于所有框架,Django,Flask,Tornado,Bottle) 一个请求到来未处理完成,后续一直等待 解决方案: ...

  8. python学习笔记-(十四)I/O多路复用 阻塞、非阻塞、同步、异步

    1. 概念说明 1.1 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可 ...

  9. Python网络编程-IO阻塞与非阻塞及多路复用

    前言 问题:普通套接字实现的服务端的缺陷 一次只能服务一个客户端!                         accept阻塞! 在没有新的套接字来之前,不能处理已经建立连接的套接字的请求 re ...

随机推荐

  1. 多线程-定时器Timer

    2019-04-1218:03:32 package 多线程.定时器Timer_重要; import java.util.Timer; import java.util.TimerTask; publ ...

  2. SSM实现图片上传管理操作

    Spring MVC 实现文件上传 时序图 利用 Spring MVC 实现文件上传功能,离不开对 MultipartResolver 的设置.MultipartResolver 这个类,你可以将其视 ...

  3. Maven环境的搭建

    1.本地仓库和apache-mavenbin.zip的下载与解压 <1.apache-mavenbin.zip下载网址 http://maven.apache.org/download.cgi ...

  4. 并发模型之Future设计模式

    一.Futrue模式 客户端发送一个长时间的请求,服务端不需等待该数据处理完成便立即返回一个伪造的代理数据(相当于商品订单,不是商品本身),用户也无需等待,先去执行其他的若干操作后,再去调用服务器已经 ...

  5. jQuery轮播图(一)轮播实现并封装

    利用面向对象自己动手写了一个封装好的jquery轮播组件,可满足一般需求,不仅使用简单且复用性高. demo:点此预览 代码地址:https://github.com/zsqosos/componen ...

  6. 初始react native遇到的问题

    转载自Andriod 使用react native时遇到的问题     打开现有项目报错: 从第一行Error可以知道是一个zip的压缩文件打不开,往下看应该是下载的Gradle文件有问题,提示也是让 ...

  7. Android SDK开发与使用的那些事儿

    前言 最近由于工作需要,将应用里的部分功能独立了出来,封装成 SDK 提供给合作伙伴使用.由于经验不足,网上也没多少写这方面内容的文章,遇到了不少的坑,决定记录下来. SDK 其实,刚说到要写SDK也 ...

  8. 下载 github 项目文件到本地方法

    下载 github 项目文件到本地方法 本篇终极,收集 3 种方法 最厉害 666 的方法 直接访问网站: 操作如下: 本地工具版下载方法 首先需要下载 git 客户端 我就不转载了,上面有客户端的使 ...

  9. 个人小项目——Java实现WC功能

    这个小项目用了两种方法解决了该功能的实现. 1.两种方法的功能和具体实现 代码可以成功运行,但是有一些情况考虑不完整,一种方法用了FileOutputStream输出流,为了解决空格无法统计问题,对文 ...

  10. CentOS 7 yum 安装 Nginx

    1.添加Nginx到YUM源 添加CentOS 7 Nginx yum资源库,打开终端,使用以下命令: sudo rpm -Uvh http://nginx.org/packages/centos/7 ...