机文章

python多线程例子python:真正的Python多线程来了!1988年,23岁巩俐和38岁张艺谋泳池边的照片,这1幕相当滑稽可笑 速看

 

每当提到互联网,我们就会想到无限的可能性和无穷的创新。现在,就让我们1起来看看最近在互联网领域有哪些令人振奋的消息和故事。

【CSDN 编者按】IBM工程师Martin Heinz发文表示,Python即将迎来了真正的多线程时刻!原文:https://martinheinz.dev/blog/97未经授权,禁止转载!作者 | Martin Heinz

责编 | 梦依丹翻译工具 | ChatGPT32岁的Python依然没有真正的并行性/并发性然而,这种情况即将发生改变,因为在即将发布的Python 3.12中引入了名为"Per-Interpreter GIL"(全局解释器锁)的新特性。

在距离发布还有几个月的时间(预计2023年10月发布),但相关代码已经有了,因此,我们可以提前了解如何使用子解释器API编写真正的并发Python代码子解释器首先,让我们来解释1下如何通过“Per-Interpreter GIL”来解决Python缺适当的并发性问题。

在Python中,GIL是1个互斥锁,它只允许1个线程控制Python解释器这意味着即使在Python中创建多个线程(例如使用线程模块),也只有1个线程会运行随着“Per-Interpreter GIL”的引入,各个Python解释器没有再共享同1个GIL。

这种隔离级别允许每个子解释器可以同时运行这意味着我们可以通过生成额外的子解释器来绕过Python的并发限制,其中每个子解释器都有自己的GIL(全局状态)更详细的说明请参见PEP 684,该文档描述了此功能/更改:https://peps.python.org/pep-0684/#per-interpreter-state

上手体验安装为使用这项最新功能,我们必须安装最新版的Python,并且需要从源码长进行构建:# https://devguide.python.org/getting-started/setup-building/

#unix-compilinggit clone https://github.com/python/cpython.gitcd cpython./configure --enable-optimizations --prefix=$(pwd)/python.12

make -s -j2./python# Python 3.12.0a7+ (heads/main:22f3425c3d, May 10 2023, 12:52:07) [GCC 11.3.0] on linux

# Type "help", "copyright", "credits" or "license"for more information.C-API在哪里?既然已经安装了最新的版本,那我们该如何使用子解释器呢?可以直接导入吗?没有,正如PEP84中所提到的:“

这是1个高级功能,专为C-API的1小部分用户设计”目前,Per-Interpreter GIL特性只能通过C-API使用,因此Python开发人员没有直接的接口可以使用这样的接口预计将随着PEP 5541起推出,如果被采纳,则应该会在Python 3.13中实现,在那之前,我们必须自己想办法实现子解。

释器通过CPython代码库中的1些零散记录,我们可以采用上面两种方法:使用_xxsubinterpreters模块,该模块是用C实现的,因此名称看起来有些奇怪由于它是用C实现的,所以开发者无法轻易地检查代码(至少没有是在 Python 中);。

或者可以利用CPython的测试模块,该模块具有用于测试的示例 Interpreter(和 Channel)类# Choose one of these:import _xxsubinterpreters 。

as interpretersfrom test.support import interpreters在接下来的演示中,我们将次要采用第二种方法我们已经找到了子解释器,但还需要从Python的测试模块中借用1些辅助函数,以便将代码传递给子解释器:。

from textwrap import dedentimport os# https://github.com/python/cpython/blob/# 15665d896bae9c3d8b60bd7210ac1b7dc533b093/Lib/test/test__xxsubinterpreters.py#L75

def_captured_script(script): r, w = os.pipe() indented = script.replace(\n, \n )

wred = dedent(f""" import contextlib with open({w}, w, encoding="utf") as spipe:

with contextlib.redirect_stdout(spipe):{indented} """)return wred, open(r, encoding=

"utf")def_run_output(interp, request, channels=None): script, rpipe = _captured_script(request)with

rpipe: interp.run(script, channels=channels)return rpipe.read()将interpreters模块上述的辅助程序组合在1起,便可以生成第1个子解释器:

from test.support import interpretersmain = interpreters.get_main()print(f"Main interpreter ID: {main}

")# Main interpreter ID: Interpreter(id=0, isolated=None)interp = interpreters.create()print(f"Sub-interpreter:

{interp}")# Sub-interpreter: Interpreter(id=1, isolated=True)# https://github.com/python/cpython/blob/

# 15665d896bae9c3d8b60bd7210ac1b7dc533b093/Lib/test/test__xxsubinterpreters.py#L236code = dedent("""

from test.support import interpreters cur = interpreters.get_current() print(cur.id)

""")out = _run_output(interp, code)print(f"All Interpreters: {interpreters.list_all()}")# All Interpreters: [Interpreter(id=0, isolated=None), Interpreter(id=1, isolated=None)]

print(f"Output: {out}") # Result of print(cur.id)# Output: 1生成和运行新解释器的1个方法是使用create函数,然后将解释器要执行的代码1起传递给_run_output辅助函数。

更简单点的方法是:interp = interpreters.create()interp.run(code)使用解释器中的run方法可是,如果我们运行上述代码中的任意1个,都会得到如下错误:Fatal。

Python error: PyInterpreterState_Delete: remaining subinterpretersPython runtime state: finalizing (tstate=0x000055b5926bf398)

为避免此类错误发生,还需要清理1些悬挂的解释器:defcleanup_interpreters():for i in interpreters.list_all():if i.id == 0: # main

continuetry: print(f"Cleaning up interpreter: {i}") i.close()except RuntimeError:

pass# already destroyedcleanup_interpreters()# Cleaning up interpreter: Interpreter(id=1, isolated=None)

# Cleaning up interpreter: Interpreter(id=2, isolated=None)线程虽然使用上述辅助函数运行代码是可行的,但使用线程模块中熟悉的接口可能更加方便:import

threadingdefrun_in_thread(): t = threading.Thread(target=interpreters.create) print(t) t.start()

print(t) t.join() print(t)run_in_thread()run_in_thread()# # # # # # 我们通过把interpreters.create函数传递给Thread,

它会自动在线程内部生成新的子解释器我们也可以结合这两种方法,并将辅助函数传递给threading.Thread:import timedefrun_in_thread(): interp = interpreters.create(isolated=。

True) t = threading.Thread(target=_run_output, args=(interp, dedent(""" import _xxsubinterpreters as _interpreters

cur = _interpreters.get_current() import time time.sleep(2) # Cant print from here, wont bubble-up to main interpreter

assert isinstance(cur, _interpreters.InterpreterID) """))) print(f"Created Thread:

{t}") t.start()return tt1 = run_in_thread()print(f"First running Thread: {t1}")t2 = run_in_thread()

print(f"Second running Thread: {t2}")time.sleep(4) # Need to sleep to give Threads time to completecleanup_interpreters()

这里,我们演示了如何使用_xxsubinterpreters模块而没有是test.support中的模块我们还在每个线程中睡眠2秒钟来模拟1些“工作”请注意,我们甚至没有必调用join()函数等待线程完成,只需在线程完成时清理解释器即可。

Channels如果我们深入研究CPython测试模块,我们还会发现有 RecvChannel 和 SendChannel 类的实现,它们类似于 Golang 中的通道(Channel)要使用它们:# https://github.com/python/cpython/blob/

# 15665d896bae9c3d8b60bd7210ac1b7dc533b093/Lib/test/test_interpreters.py#L583r, s = interpreters.create_channel()

print(f"Channel: {r}, {s}")# Channel: RecvChannel(id=0), SendChannel(id=0)orig = bspams.send_nowait(orig)

obj = r.recv()print(f"Received: {obj}")# Received: bspamcleanup_interpreters()# Need clean up, otherwise:

# free(): invalid pointer# Aborted (core dumped)这个例子展示了如何创建1个带有receiver(r)和sender(s)端的通道我们可以使用send_nowait将数据传递给发送方,并使用recv函数在另1侧读取。

它这个通道实际上只是另1个子解释器 - 所以之前1样 - 我们需要在完成后进行清理深入挖掘最后,如果我们想要干扰或调整在C代码中设置的子解释器选项,那么可以使用test.support模块中的代码,具体来说就是run_in_subinterp_with_config:。

importtest.supportdefrun_in_thread(script):test.support.run_in_subinterp_with_config(script,use_main_obmalloc

=True,allow_fork=True,allow_exec=True,allow_threads=True,allow_daemon_threads=False,check_multi_interp_extensions

=False,own_gil=True,)code = dedent(f"""fromtest.support import interpreterscur = interpreters.get_current()

print(cur)""")run_in_thread(code)# Interpreter(id=7, isolated=None)run_in_thread(code)# Interpreter(id=8, isolated=None)

这个函数是1个Python API,用于调用C函数它提供了1些子解释器选项,如own_gil,指定子解释器是否应该拥有自己的GIL总结话虽如此——也正如你所看到的,API调用并没有简单,除非你已具备C语言专业知识,并且又迫切想要使用字解释器,否则建议还是等待Python 3.13的发布。

或者您可以尝试extrainterpreters项目,该项目提供更友好的Python API以便使用子解释器。

如果您觉得这篇文章对您有所帮助,请在下方留下您的评论,让更多人看到。

为您推荐

python多线程例子python:真正的Python多线程来了!1988年,23岁巩俐和38岁张艺谋泳池边的照片,这1幕相当滑稽可笑 速看

python多线程例子python:真正的Python多线程来了!1988年,23岁巩俐和38岁张艺谋泳池边的照片,这1幕相当滑稽可笑 速看

每当提到互联网,我们就会想到无限的可能性和无穷的创新。现在,就让我们1起来看看最近在互联网领域有哪...

2023-06-08 栏目:编程控
最近发表

当前非电脑浏览器正常宽度,请使用移动设备访问本站!