目录
PEP 554 Stdlib中的多个解释器
概述
自1.5版(1997年)以来,CPython一直支持同一流程中的多个解释器(AKA“子解释器”)。该功能已通过C-API提供。[c-api]子解释器在彼此相对隔离的情况下运行,这有助于新的并发替代方法。
本提案介绍了stdlib解释器模块。该模块为临时模块。它公开了C-API已经提供的子企业的基本功能,以及在解释器之间共享数据的新(基本)功能。
为避免事先出现任何混淆:该PEP与子企业之间停止共享GIL的任何努力无关。该提案最多允许用户利用GIL的任何工作成果。这里的立场是,将子企业暴露于Python代码是值得的,即使它们仍然共享GIL。
解释器模块The “interpreters” Module
解释器模块将添加到stdlib中。为了帮助扩展模块的作者,将在扩展Python文档中添加一个新页面。有关这两个方面的更多信息。
解释器模块将为子企业提供一个高级接口,并包装一个新的低级解释器(与线程模块相同)。
除了公开现有的(在CPython中)子解释器支持外,该模块还将提供一种在解释器之间共享数据的机制。该机制以“通道”为中心,类似于队列和管道。
请注意,对象不会在解释器之间共享,因为它们与创建它们的解释器绑定。相反,对象的数据在解释器之间传递。
最初,仅支持以下类型的共享:
没有一个
扫描二维码关注公众号,回复: 15230691 查看本文章字节
字符串
整数
PEP 554通道
稍后将添加对其他基本类型(例如bool、float、Ellipsis)的支持。
解释器模块的API概述
下面是解释器模块的API概述。有关建议的类和函数的更深入解释,请参阅下面的“解释器”模块API部分。
list_all() -> [Interpreter]
获取所有现有解释器。
get_current() -> Interpreter
获取当前正在运行的解释器。
get_main() -> Interpreter
找到主解释器。
create(*, isolated=True) -> Interpreter
初始化一个新的(空闲的)Python解释器。
class Interpreter(id)
单个解释器
.id
解释器的ID(只读)。
.isolated
解释器的模式(只读)。
.is_running() -> bool
解释器当前是否正在执行代码?
.close()
完成并销毁解释器。
.run(src_str, /, *, channels=None)
在解释器中运行给定的源代码。
(这将阻止当前线程,直到完成。)
RunFailedError
基类RuntimeError
Interpreter.run()导致未捕获的异常。
is_shareable(obj) -> Bool
是否可以共享对象的数据解释器。
create_channel() -> (RecvChannel, SendChannel)
创建用于传递的新通道解释器之间的数据。
list_all_channels() -> [(RecvChannel, SendChannel)]
获取所有打开的频道。
class RecvChannel(id)
信道的接收端。
.id
频道的唯一ID。
.recv() -> object
从频道中获取下一个对象,
如果没有发送,等待。
.recv_nowait(default=None) -> object
类似recv(),但返回默认值
而不是等待。
class SendChannel(id)
信道的发送端。
.id
频道的唯一ID。
.send(obj)
将对象(即其数据)发送到
信道的接收端并等待。
.send_nowait(obj)
类似send(),但如果未收到则返回False。
ChannelError
基类 Exception
通道相关异常的基类。
ChannelNotFoundError
基类ChannelError
找不到标识的频道。
ChannelEmptyError
基类 ChannelError
通道意外为空。
ChannelNotEmptyError
基类 ChannelError
通道意外不为空。
NotReceivedError
基类 ChannelError
没有任何东西等待接收发送的对象。
示例
运行隔离代码
interp = interpreters.create()
print('before')
interp.run('print("during")')
print('after')
在线程中运行
interp = interpreters.create()
def run():
interp.run('print("during")')
t = threading.Thread(target=run)
print('before')
t.start()
print('after')
预填充解释器
interp = interpreters.create()
interp.run(tw.dedent("""
import some_lib
import an_expensive_module
some_lib.set_up()
"""))
wait_for_request()
interp.run(tw.dedent("""
some_lib.handle_request()
"""))
处理异常
interp = interpreters.create()
try:
interp.run(tw.dedent("""
raise KeyError
"""))
except interpreters.RunFailedError as exc:
print(f"got the error from the subinterpreter: {
exc}")
重新引发异常
interp = interpreters.create()
try:
try:
interp.run(tw.dedent("""
raise KeyError
"""))
except interpreters.RunFailedError as exc:
raise exc.__cause__
except KeyError:
print("got a KeyError from the subinterpreter")
使用频道同步
interp = interpreters.create()
r, s = interpreters.create_channel()
def run():
interp.run(tw.dedent("""
reader.recv()
print("during")
"""),
shared=dict(
reader=r,
),
)
t = threading.Thread(target=run)
print('before')
t.start()
print('after')
s.send(b'')
共享文件描述符
interp = interpreters.create()
r1, s1 = interpreters.create_channel()
r2, s2 = interpreters.create_channel()
def run():
interp.run(tw.dedent("""
fd = int.from_bytes(
reader.recv(), 'big')
for line in os.fdopen(fd):
print(line)
writer.send(b'')
"""),
shared=dict(
reader=r,
writer=s2,
),
)
t = threading.Thread(target=run)
t.start()
with open('spamspamspam') as infile:
fd = infile.fileno().to_bytes(1, 'big')
s.send(fd)
r.recv()
通过封送传递对象
interp = interpreters.create()
r, s = interpreters.create_channel()
interp.run(tw.dedent("""
import marshal
"""),
shared=dict(
reader=r,
),
)
def run():
interp.run(tw.dedent("""
data = reader.recv()
while data:
obj = marshal.loads(data)
do_something(obj)
data = reader.recv()
"""))
t = threading.Thread(target=run)
t.start()
for obj in input:
data = marshal.dumps(obj)
s.send(data)
s.send(None)
通过pickle传递对象
interp = interpreters.create()
r, s = interpreters.create_channel()
interp.run(tw.dedent("""
import pickle
"""),
shared=dict(
reader=r,
),
)
def run():
interp.run(tw.dedent("""
data = reader.recv()
while data:
obj = pickle.loads(data)
do_something(obj)
data = reader.recv()
"""))
t = threading.Thread(target=run)
t.start()
for obj in input:
data = pickle.dumps(obj)
s.send(data)
s.send(None)
运行模块
interp = interpreters.create()
main_module = mod_name
interp.run(f'import runpy; runpy.run_module({
main_module!r})')
作为脚本运行(包括zip存档和目录)
interp = interpreters.create()
main_script = path_name
interp.run(f"import runpy; runpy.run_path({
main_script!r})")
在线程池执行器中运行
interps = [interpreters.create() for i in range(5)]
with concurrent.futures.ThreadPoolExecutor(max_workers=len(interps)) as pool:
print('before')
for interp in interps:
pool.submit(interp.run, 'print("starting"); print("stopping")'
print('after')