Python多线程笔记(二)
Lock对象
原语锁(互斥锁)是一个同步原语,状态是"已锁定"或者"未锁定"之一.两个方法acquire()和release()用于修改锁的状态.如果状态为已锁定,
尝试获取锁将被阻塞,直到锁被释放为止.如果有多个线程等待获取锁,当锁被释放时,只有一个线程能获取它,等待线程获得锁的顺序没有定义.
使用下面的构造函数可以创建新的Lock实例:
Lock()
创建新的Lock对象,初始状态为未锁定 Lock实例lock支持一下方法 lock.acquire([blocking])
获取锁,如果有必要,需要阻塞到锁释放为止.如果提供blocking参数并将它设为False,当无法获取锁时立即返回False,
如果成功获取锁则返回True. lock.release()
释放一个锁.当锁处于未锁定状态时,或者从与原本调用acquire()方法的线程不同的线程调用此方法,将出现错误
# 未加锁,Windows下运行不会出现混乱,在Linux下运行就会发行结果出现混乱 import threading
import time num = def show():
global num
time.sleep()
num +=
print(num) for i in range():
t = threading.Thread(target=show)
t.start() print('thread stop')
Linux下的结果(多运行几遍就会发现结果混乱)
# 加锁
import threading
import time clock = threading.Lock()
num = def show():
clock.acquire()
global num
time.sleep()
num +=
print(num)
clock.release() for i in range():
t = threading.Thread(target=show)
t.start() print('thread stop')
Rlock对象
可重入锁是一个类似于Lock对象的同步原语,但同一个线程可以多次获取它。这允许拥有锁的线程执行嵌套的acquire()和release()操作。在这种
情况下,只有对外面的release()操作才能将锁重置为未锁定状态。
使用以下的构造函数可以创建一个新的RLock对象: RLock()
创建新的可重入锁对象。RLock对象rlock支持一下方法。 rlock.acquire([blocking])
获取锁,如果有必要,需要阻塞到锁被释放为止。如果没有线程拥有锁,它将被锁定,而且递归级别被置为1.如果此线程已经拥有锁,锁的递归级别加1,
而且函数立即返回。 rlock.release()
通过减少锁的递归级别来释放它。如果在减值后递归级别为0,锁将被重置为未锁定状态。否则,锁将保持以锁定状态。只能由目前拥有锁的线程来调用此函数。
在使用诸如Lock,RLcok或Semphore之类的锁原语时,必须多加小心。锁的错误管理经常导致死锁或竞争条件。依赖的代码应该保证出现异常时正确地释放锁。
典型的代码如下:
try:
lock.acquire()
# 关键部分
statements
...
finally:
lock.release() # 另外,所有种类的锁还支持上下文管理协议(写起来更简洁些) with lock:
# 关键部分
# with语句自动获取锁,并且在控制流离开上下文时自动释放锁
# 此外,编写代码时一般应该避免同时获取多个锁例如:
with lock_A:
# 关键部分
statements ...
with lock_B:
# B的关键部分 ...
这通常很容易导致应用程序神秘死锁。尽管有几种策略可以避免出现这种情况(如分层锁定),但最好在编写代码时就避免这种做法
信号量
信号量是一个基于计数器的同步原语,每一次调用acquire()方法时此计数器减1,每次调用release()方法时此计数器加1.如果计数器为0,acquire()方法将会阻塞,
直到其他线程调用release()方法为止。
Semaphore([value])
创建一个新的信号量。value是计数器的初始值。如果省略此参数,计数器的值将被置为1. Semaphore实例s支持一下方法 s.acquire([blocking])
获取信号量。如果进入时内部计数器大于0,此方法将把它的值减1,然后立即返回。如果它的值为0,此方法将阻塞,知道另一个线程调用release()方法为止。
blocking参数的行为与Lock和RLock对象中描述的相同
s.release()
通过将内部计数器的值加1来释放一个信号量。如果计数器为0,而且另一个线程正在等待,该线程将被唤醒。如果有多个线程正在等待,只能从它的acquire()
调用返回其中一个。线程释放的顺序并不确定 BoundedSemaphore([value])
创建一个新的信号量。Value是计数器的初始值。如果省略此参数,计数器的值将被置为1.BounderSemaphore的工作方式与Semaphore完全相同,
但release()操作的次数不能超过acquire()操作的次数
信号量与互斥锁之间的微妙差别在于:信号量可以用于发信号。例如,可以从不同的线程调用acquire()和release()方法,以便在生产者和消费者线程之间进行通信。
import threading
import time
produced = threading.Semaphore(3)
consumed = threading.Semaphore(2) def producer():
for i in range(10):
consumed.acquire()
time.sleep(3)
print('-----------------', time.ctime())
consumed.release() def consumer():
for j in range(10,20):
produced.acquire()
time.sleep(3)
print('=================', time.ctime())
consumed.release() for i in range(20):
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start()
t2.start()
运行结果
----------------- Fri Dec 14 16:11:08 2018
================= Fri Dec 14 16:11:08 2018
----------------- Fri Dec 14 16:11:08 2018
================= Fri Dec 14 16:11:08 2018
================= Fri Dec 14 16:11:08 2018
----------------- Fri Dec 14 16:11:11 2018
----------------- Fri Dec 14 16:11:11 2018
----------------- Fri Dec 14 16:11:11 2018
----------------- Fri Dec 14 16:11:11 2018
----------------- Fri Dec 14 16:11:11 2018
----------------- Fri Dec 14 16:11:14 2018
事件
事件用于在线程之间通信。一个线程发出“事件”信号,一个或多个其他线程等待它,Event实例管理着一个内部标志,可以使用set()方法将它置为True,或者
使用clear()方法将它重置为False。wait()方法将阻塞,直到标志为True
Event()
创建新的Event实例,并将内部标志置为False。Event实例e支持一下方法 e.is_set()
只有当内部标志为True时才返回True。在老式代码中此方法叫isSet() e.set()
内部标志为True。等待它变为True的所有线程都将被唤醒 e.clear()
将内部标志重置为False e.wait([timeout])
阻塞直到内部标志为True。如果进入时内部标志为True,此方法将立即返回。否则它将阻塞,直到另一个线程调用set()方法将标志置为True,或者直到出现可选的
超时。timeout是一个浮点数,用于指定以秒为单位的超时期限
import threading
import time event = threading.Event() def chihuoguo():
# 等待事件,进入等待阻塞状态
print('%s 进入准备状态' % threading.currentThread().getName())
time.sleep(1)
event.wait()
# 收到事件后进入运行状态
print('%s 开始运行' % threading.currentThread().getName()) # 设置线程组
threads = [] # 创建新线程
thread1 = threading.Thread(target=chihuoguo, name='one')
thread2 = threading.Thread(target=chihuoguo, name='two') # 添加到线程组
threads.append(thread1)
threads.append(thread2) # 开启线程
for thread in threads:
thread.start() time.sleep(0.1)
# 发送事件通知
print('event start')
event.set()
使用event.clear()
import threading
import time event = threading.Event() def chihuoguo():
for i in range(1, 5):
# 等待事件,进入等待阻塞状态
print('%s %s 进入准备状态' % (threading.currentThread().getName(), i))
time.sleep(1)
event.wait() if i == 3:
event.clear()
# 收到事件后进入运行状态
print('%s %s 开始运行' % (threading.currentThread().getName(), i)) # 设置线程组
threads = [] # 创建新线程
thread1 = threading.Thread(target=chihuoguo, name='one')
thread2 = threading.Thread(target=chihuoguo, name='two') # 添加到线程组
threads.append(thread1)
threads.append(thread2) # 开启线程
for thread in threads:
thread.start() time.sleep(0.1) # event.clear()
# 发送事件通知
print('event start')
event.set()
尽管Event对象可用于给其他线程发信号,但不应该使用它们来实现在生成者/消费者问题中十分典型的通知。例如,应该避免写出下面这样的代码
evt = threading.Event() def producer():
while True:
# 生产者
...
evt.signal() def consumer():
while True:
# 一个等待项
evt.wait()
# 消费项
...
# 清除事件并再次等待
evt.clear()
这段代码并不可靠,因为在evt.wait()和evt.clear()操作之间,生产者可能产生了一个新项。但是,通过清除事件,在生产者创建一个新项之前,消费者可能看不到
这个新项。最好的情况是,程序将经过一段很短的停滞,对项的处理被莫名其妙推迟了。最坏的情况是,由于事件信号丢失,整个程序将挂起。要解决这类问题最好使用
条件变量
Python多线程笔记(二)的更多相关文章
- Python 学习笔记二
笔记二 :print 以及基本文件操作 笔记一已取消置顶链接地址 http://www.cnblogs.com/dzzy/p/5140899.html 暑假只是快速过了一遍python ,现在起开始仔 ...
- python学习笔记(二)、字符串操作
该一系列python学习笔记都是根据<Python基础教程(第3版)>内容所记录整理的 1.字符串基本操作 所有标准序列操作(索引.切片.乘法.成员资格检查.长度.最小值和最大值)都适用于 ...
- Python面试笔记二
一.算法 1.归并排序 2.快速排序 3.算法复杂度 4.哈希表数据结构 二.数据库 1.设计一个用户关注系统的数据库表 1.设计一个用户关注系统的数据库表,写三个相关的SQL语句两张表,一张user ...
- python多线程学习二
本文希望达到的目标: 多线程同步原语:互斥锁 多线程队列queue 线程池threadpool 一.多线程同步原语:互斥锁 在多线程代码中,总有一些特定的函数或者代码块不应该被多个线程同时执行,通常包 ...
- python多线程(二)
开启线程的两种方式 #方式一from threading import Threadimport timedef sayhi(name): time.sleep(2) print('%s sa ...
- Python学习笔记二
---恢复内容开始--- 一. python几种数据类型的比较. 从以下几个方面比较: 1. 是否可变. 不可变类型:变量的值可以发生变化,id也变了,相当于创建了一个新的对象,所以一修改值,id就变 ...
- Python学习笔记(二)
标识符和关键字 1,邮箱的Python标识符是任意长度的非空字符序列(引导字符+后续字符.) python标识符必须符合两条规则--标识符区分大小写 (1)只要是unicode编码字母都可以充当引导字 ...
- Python基础笔记(二)
1. List和Tuple List和Tuple是Python的内置的数据类型,区别在于可变和不可变,List用[]表示,Tuple用()表示,它们之间可以相互转换: # List to Tuple ...
- python学习笔记(二):python数据类型
上一篇博客写了python的入门和简单流程控制,这次写python的数据类型和各种数据类型的内置方法.一.数据类型是什么鬼?计算机顾名思义就是可以做数学计算的机器,因此,计算机程序理所当然地可以处理各 ...
随机推荐
- 手动编译ts的经过
动机 以前写ts或者es6,都是用在脚手架搭建的项目中,比如vue和react,当时当我识图写一个ts的demo的,我还要创建一个完整的vue或者react项目?明显不合适,那就要研究一下如何手动搭建 ...
- Thread.interrupt()源码跟踪
1 JDK源码跟踪 // java.lang.Thread public void interrupt() { if (this != Thread.currentThread()) checkAcc ...
- 在论坛中出现的比较难的sql问题:13(循环替换问题 过滤各种标点符号)
原文:在论坛中出现的比较难的sql问题:13(循环替换问题 过滤各种标点符号) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路. 去掉一个字段中的标点符号的SQL语句怎么写 ...
- ajax实现文件上传,多文件上传,追加参数
1.html部分实现代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...
- git bash push 本地的commit到远程 -- ssh keys设置
1. 检查是否已经创建 ssh keys git bash 下,cd ~/.ssh 如何出现“No such file or directory”,则表示需要创建一个ssh keys. 2. 创建新 ...
- 【前端适配】vw单位移动端适配方案
近些年移动端的强势崛起,导致移动端适配越来越重要,个人之前一直使用的是rem进行适配,但是发现并不是非常完美,给力的是大漠老师写了一篇<如何在Vue项目中使用vw实现移动端适配>,比较完美 ...
- 采集代理ip 地址【西刺,快代理】
# 嗯,...因为经常需要使用代理去抓一点东西,就有了下面一段代码,第一版不是很好,后面用到了再来优化 import re,pymysql,time,redis from urllib.request ...
- Windows 如何录屏
从Windows10开始,Windows开始自带了录屏功能(XBOX附带的).本来是方便游戏录制,但日常的录制也不在话下. 快捷键:Win + G 打开XBOX的录制工具 在打开录制工具后 Win + ...
- javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certificatio
场景:Java调用PHP接口,代码部署在服务器上后,调用报错,显示PHP服务器那边证书我这边服务器不信任(我猜的). 异常信息: 2019-08-06 14:00:09,102 [http-nio-4 ...
- python之匿名函数、递归与二分法
一.匿名函数 什么是匿名函数? 顾名思义就是没有名字的函数,在我们声明一个函数时会想起个什么函数名好,这个问题我想有时候会困惑大家的吧? def func(): #正常函数声明 pass prin ...