Python 解释器的主要作用是将我们在 .py 文件中写好的代码交给机器去执行,比较常见的解释器包括如下几种:
CPython:官方解释器,我们从官网下载安装后获得的就是这个解释器,它使用 C 语言开发,是使用范围最广泛的 Python 解释器。
Jython:由 Java 编写,它可以将 Python 代码编译成 Java 字节码,再由 JVM 执行对应的字节码。
IronPython:与 Jython 类似,它由 C# 编写,是运行在 .Net 平台上的解释器。
IPython:基于 CPython 的一个交互式解释器,它主要增强了 CPython 的交互方式。
PyPy:采用了 JIT 技术,它是一个关注执行速度的 Python 解释器,该解释器可以明显提升 Python 代码的执行速度。
GIL 全称 global interpreter lock,中文译为全局解释器锁,CPython 解释器就是通过 GIL 机制来确保同一时刻只有一个线程执行 Python 代码的,这样做十分方便的帮助 CPython 解决了并发访问的线程安全问题,但却牺牲了在多处理器上的并行性,所以 CPython 解释器下的多线程并不是真正意义上的多线程。
Python(CPython) 提供了 _thread 和 threading 两个线程模块,_thread 是低级模块,threading 对 _thread 进行了封装,提高了 _thread 原有功能的易用性以及扩展了新功能,通常我们只需要使用 threading 模块就可以了,这里我们也只对 threading 模块进行详细介绍。
首先,我们来看一下 threading 模块的直接方法和属性。
threading.enumerate()
以列表形式返回当前所有存活的 threading.Thread 对象。
threading.active_count()
返回当前存活的 threading.Thread 对象,等于 len(threading.enumerate())。
threading.current_thread()
返回当前对应调用者控制的 threading.Thread 对象,如果调用者的控制线程不是利用 threading 创建,则会返回一个功能受限的虚拟线程对象。
threading.get_ident()
返回当前线程的线程标识符,它是一个非零的整数,其值没有直接含义,它可能会在线程退出,新线程创建时被复用。
threading.main_thread()
返回主线程对象,一般情况下,主线程是 Python 解释器开始时创建的线程。
threading.stack_size([size])
返回创建线程时用的堆栈大小,可选参数 size 指定之后新建线程的堆栈大小,size 值需要为 0 或者最小是 32768(32KiB)的一个正整数,如不指定 size,则默认为 0。
threading.get_native_id()
返回内核分配给当前线程的原生集成线程 ID,其值是一个非负整数。
threading.TIMEOUT_MAX
指定阻塞函数(如:Lock.acquire(), Condition.wait() …)中形参 timeout 允许的最大值,传入超过这个值的 timeout 会抛出 OverflowError 异常。
先了解一下 Python 守护线程基本概念。
守护线程:当一个线程被标记为守护线程时,Python 程序会在剩下的线程都是守护线程时退出,即等待所有非守护线程运行完毕;守护线程在程序关闭时会突然关闭,可能会导致资源不能被正确释放的的问题,如:已经打开的文档等。
非守护线程:通常我们创建的线程默认就是非守护线程,Python 程序退出时,如果还有非守护线程在运行,程序会等待所有非守护线程运行完毕才会退出。
threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
创建线程对象,参数说明如下所示。
group:通常默认即可,作为日后扩展 ThreadGroup 类实现而保留。
target:用于 run() 方法调用的可调用对象,默认为 None。
name:线程名称,默认是 Thread-N 格式构成的唯一名称,其中 N 是十进制数。
args:用于调用目标函数的参数元组,默认为 ()。
kwargs:用于调用目标函数的关键字参数字典,默认为 {}。
daemon:设置线程是否为守护模式,默认为 None。
看一下线程对象 threading.Thread 的方法和属性。
start():启动线程。
run():线程执行具体功能的方法。
join(timeout=None):当 timeout 为 None 时,会等待至线程结束;当 timeout 不为 None 时,会等待至 timeout 时间结束,单位为秒。
is_alive():判断线程是否存活。
getName():返回线程名。
setName():设置线程名。
isDaemon():判断线程是否为守护线程。
setDaemon():设置线程是否为守护线程。
name:线程名。
ident:线程标识符。
daemon:线程是否为守护线程。
我们可以通过实例化 threading.Thread 来创建线程,也可以使用继承 threading.Thread 的子类来创建。
实例化 threading.Thread
import threading
import time
def target(sleep):
time.sleep(sleep)
print('当前线程为:', threading.current_thread().name,' ', 'sleep:', sleep)
if __name__ == '__main__':
t1 = threading.Thread(name='t1', target=target, args=(1,))
t2 = threading.Thread(name='t2', target=target, args=(2,))
t1.start()
t2.start()
print('主线程结束')
继承 threading.Thread
import threading
import time
class MyThread(threading.Thread):
def __init__(self, sleep, name):
super().__init__()
self.sleep = sleep
self.name = name
def run(self):
time.sleep(self.sleep)
print('name:' + self.name)
if __name__ == '__main__':
t1 = MyThread(1, 't1')
t2 = MyThread(1, 't2')
t1.start()
t2.start()
同一变量在多线之间是共享的,任何一个变量都可以被所有线程修改,当多个线程一起修改同一变量时,很可能互相冲突得不到正确的结果,造成线程安全问题。通过示例看一下:
import threading
a = 5
def oper(b):
global a
a = a - b
a = a + b
def target(b):
for i in range(100000):
oper(b)
if __name__ == '__main__':
m = 10
while m > 0:
t1 = threading.Thread(target=target, args=(1,))
t2 = threading.Thread(target=target, args=(2,))
t1.start()
t2.start()
t1.join()
t2.join()
print(a)
m = m - 1
执行结果:
5
5
5
6
6
正常情况下,oper(b) 操作会使 a 的值保持不变,但从多线程的执行结果来看,我们发现出现了错误的结果,并且每次执行的结果可能不同,通常这种问题我们可以使用加锁的方式解决。
threading.Lock
实现原始锁对象的类,一旦一个线程获得一个锁,会阻塞随后尝试获得锁的线程,直到它被释放,通常称其为互斥锁,它是由 _thread 模块直接扩展实现的。它具有如下方法:
acquire(blocking=True, timeout=-1):可以阻塞或非阻塞地获得锁,参数 blocking 用来设置是否阻塞,timeout 用来设置阻塞时间,当 blocking 为 False 时 timeout 将被忽略。
release():释放锁。
locked():判断是否获得了锁,如果获得了锁则返回 True。
threading.RLock
可重入锁(也称递归锁)类,一旦线程获得了重入锁,同一个线程再次获取它将不阻塞,重入锁必须由获取它的线程释放。它具有如下方法:
acquire(blocking=True, timeout=-1):解释同上。
release():解释同上。
我们对上述代码进行加锁操作,如下所示:
import threading
# 创建锁
lock = threading.Lock()
a = 5
def oper(b):
# 获取锁
lock.acquire()
global a
a = a - b
a = a + b
# 释放锁
lock.release()
def target(b):
for i in range(100000):
oper(b)
if __name__ == '__main__':
m = 5
while m > 0:
t1 = threading.Thread(target=target, args=(1,))
t2 = threading.Thread(target=target, args=(2,))
t1.start()
t2.start()
t1.join()
t2.join()
print(a)
m = m - 1
执行结果:
5
5
5
5
5
我们可以尝试多次执行,现在每次都可以获得正确的结果了。
条件对象总是与某种类型的锁对象相关联,锁对象可以通过传入获得,或者在缺省的情况下自动创建。
threading.Condition(lock=None)
实现条件对象的类。它具有如下方法:
acquire(*args):请求底层锁。
release():释放底层锁。
wait(timeout=None):等待直到被通知或发生超时。
wait_for(predicate, timeout=None):等待直到条件计算为 True,predicate 是一个可调用对象且它的返回值可被解释为一个布尔值。
notify(n=1):默认唤醒一个等待该条件的线程。
notify_all():唤醒所有正在等待该条件的线程。
使用条件对象的典型场景是将锁用于同步某些共享状态的权限,那些关注某些特定状态改变的线程重复调用 wait() 方法,直到所期望的改变发生;对于修改状态的线程,它们将当前状态改变为可能是等待者所期待的新状态后,调用 notify() 方法或者 notify_all() 方法。
import time
import threading
# 创建条件对象
c = threading.Condition()
privilege = 0
def getPri():
global privilege
c.acquire()
c.wait()
print(privilege)
c.release()
def updPri():
time.sleep(5)
c.acquire()
global privilege
privilege = 1
c.notify()
c.release()
if __name__ == '__main__':
t1 = threading.Thread(target=getPri)
t2 = threading.Thread(target=updPri)
t1.start()
t2.start()
和锁机制一样,信号量机制也是一种实现线程同步的机制,不过它比锁多了一个计数器,这个计数器主要用来计算当前剩余的锁的数量。
threading.Semaphore(value=1)
信号量实现类,可选参数 value 赋予内部计数器初始值,默认值为 1 。它具有如下方法:
acquire(blocking=True, timeout=None):获取一个信号量,参数 blocking 用来设置是否阻塞,timeout 用来设置阻塞时间。
release():释放一个信号量,将内部计数器的值增加1。
import threading
# 创建信号量对象
s = threading.Semaphore(10)
a = 5
def oper(b):
# 获取信号量
s.acquire()
global a
a = a - b
a = a + b
# 释放信号量
s.release()
def target(b):
for i in range(100000):
oper(b)
if __name__ == '__main__':
m = 5
while m > 0:
t1 = threading.Thread(target=target, args=(1,))
t2 = threading.Thread(target=target, args=(2,))
t1.start()
t2.start()
t1.join()
t2.join()
print(a)
m = m - 1
一个线程发出事件信号,其他线程等待该信号,这是最简单的线程之间通信机制之一。
threading.Event
实现事件对象的类。它有如下方法:
is_set():当内部标志为 True 时返回 True。
set():将内部标志设置为 True。
clear():将内部标志设置为 False。
wait(timeout=None):阻塞线程直到内部变量为 True。
import time
import threading
# 创建事件对象
event = threading.Event()
def dis_class():
time.sleep(5)
event.wait()
print('同学们下课了')
def bell():
time.sleep(3)
print('下课铃声响了')
event.set()
if __name__ == '__main__':
t1 = threading.Thread(target=bell)
t2 = threading.Thread(target=dis_class)
t1.start()
t2.start()
t1.join()
t2.join()
参考:
https://docs.python.org/3.8/library/threading.html#barrier-objects
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Pythonconditionalassignmentoperator对于这样一个简单的问题表示歉意,但是谷歌搜索||=并不是很有帮助;)Python中是否有与Ruby和Perl中的||=语句等效的语句?例如:foo="hey"foo||="what"#assignfooifit'sundefined#fooisstill"hey"bar||="yeah"#baris"yeah"另外,类似这样的东西的通用术语是什么?条件分配是我的第一个猜测,但Wikipediapage跟我想的不太一样。
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
我想解析一个已经存在的.mid文件,改变它的乐器,例如从“acousticgrandpiano”到“violin”,然后将它保存回去或作为另一个.mid文件。根据我在文档中看到的内容,该乐器通过program_change或patch_change指令进行了更改,但我找不到任何在已经存在的MIDI文件中执行此操作的库.他们似乎都只支持从头开始创建的MIDI文件。 最佳答案 MIDIpackage会为您完成此操作,但具体方法取决于midi文件的原始内容。一个MIDI文件由一个或多个音轨组成,每个音轨是十六个channel中任何一个上的
本文主要介绍在使用Selenium进行自动化测试或者任务时,对于使用了iframe的页面,如何定位iframe中的元素文章目录场景描述解决方案具体代码场景描述当我们在使用Selenium进行自动化测试的时候,可能会遇到一些界面或者窗体是使用HTML的iframe标签进行承载的。对于iframe中的标签,如果直接查找是无法找到的,会抛出没有找到元素的异常。比如近在咫尺的例子就是,CSDN的登录窗体就是使用的iframe,大家可以尝试通过F12开发者模式查看到的tag_name,class_name,id或者xpath来定位中的页面元素,会抛出NoSuchElementException异常。解决
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
我正在尝试使用ruby编写一个双线程客户端,一个线程从套接字读取数据并将其打印出来,另一个线程读取本地数据并将其发送到远程服务器。我发现的问题是Ruby似乎无法捕获线程内的错误,这是一个示例:#!/usr/bin/rubyThread.new{loop{$stdout.puts"hi"abc.putsefsleep1}}loop{sleep1}显然,如果我在线程外键入abc.putsef,代码将永远不会运行,因为Ruby将报告“undefinedvariableabc”。但是,如果它在一个线程内,则没有错误报告。我的问题是,如何让Ruby捕获这样的错误?或者至少,报告线程中的错误?
ValidPalindromeGivenastring,determineifitisapalindrome,consideringonlyalphanumericcharactersandignoringcases. [#125]Example:"Aman,aplan,acanal:Panama"isapalindrome."raceacar"isnotapalindrome.Haveyouconsiderthatthestringmightbeempty?Thisisagoodquestiontoaskduringaninterview.Forthepurposeofthisproblem