真正的Python多线程来了!

142bfda51a422c38cdd04eb8bb23bd4e.gif

【CSDN 编者按】IBM工程师Martin Heinz发文表示,Python即将迎来了真正的多线程时刻!

原文:https://martinheinz.dev/blog/97

未经授权,禁止转载!

作者 | Martin Heinz  责编 | 梦依丹

翻译工具 | ChatGPT

32岁的Python依然没有真正的并行性/并发性。然而,这种情况即将发生改变,因为在即将发布的Python 3.12中引入了名为"Per-Interpreter GIL"的新特性。在距离发布还有几个月的时间(预计2023年10月发布),但相关代码已经有了,因此,我们可以提前了解如何使用子解释器API编写真正的并发Python代码。

9f9ec18f5481288ca71c57aecb732acf.png

子解释器

首先,让我们来解释一下如何通过“Per-Interpreter GIL”来解决Python缺乏适当的并发性问题。

在Python中,GIL是一个互斥锁,它只允许一个线程控制Python解释器。这意味着即使在Python中创建多个线程(例如使用线程模块),也只有一个线程会运行。

随着“Per-Interpreter GIL”的引入,各个Python解释器不再共享同一个GIL。这种隔离级别允许每个子解释器可以同时运行。这意味着我们可以通过生成额外的子解释器来绕过Python的并发限制,其中每个子解释器都有自己的GIL。

更详细的说明请参见PEP 684,该文档描述了此功能/更改:https://peps.python.org/pep-0684/#per-interpreter-state

b6227ad22781eef22e247228e391ddfb.png

上手体验

安装

为使用这项最新功能,我们必须安装最新版的Python,并且需要从源码上进行构建:

# https://devguide.python.org/getting-started/setup-building/#unix-compiling
git clone https://github.com/python/cpython.git
cd cpython./configure --enable-optimizations --prefix=$(pwd)/python-3.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在哪里?

既然已经安装了最新的版本,那我们该如何使用子解释器呢?可以直接导入吗?不,正如PEP-684中所提到的:“这是一个高级功能,专为C-API的一小部分用户设计。”

目前,Per-Interpreter GIL特性只能通过C-API使用,因此Python开发人员没有直接的接口可以使用。这样的接口预计将随着PEP 554一起推出,如果被采纳,则应该会在Python 3.13中实现,在那之前,我们必须自己想办法实现子解释器。

通过CPython代码库中的一些零散记录,我们可以采用下面两种方法:

使用_xxsubinterpreters模块,该模块是用C实现的,因此名称看起来有些奇怪。由于它是用C实现的,所以开发者无法轻易地检查代码(至少不是在 Python 中);

或者可以利用CPython的测试模块,该模块具有用于测试的示例 Interpreter(和 Channel)类。

# Choose one of these:
import _xxsubinterpreters as interpreters
from test.support import interpreters

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

from textwrap import dedent
import 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                ')wrapped = dedent(f"""import contextlibwith open({w}, 'w', encoding="utf-8") as spipe:with contextlib.redirect_stdout(spipe):{indented}""")return wrapped, open(r, encoding="utf-8")def _run_output(interp, request, channels=None):script, rpipe = _captured_script(request)with rpipe:interp.run(script, channels=channels)return rpipe.read()

将interpreters模块与上述的辅助程序组合在一起,便可以生成第一个子解释器:

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#L236
code = dedent("""from test.support import interpreterscur = 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

生成和运行新解释器的一个方法是使用create函数,然后将解释器与要执行的代码一起传递给_run_output辅助函数。

更简单点的方法是:

interp = interpreters.create()
interp.run(code)

使用解释器中的run方法。可是,如果我们运行上述代码中的任意一个,都会得到如下错误:

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

为避免此类错误发生,还需要清理一些悬挂的解释器:

def cleanup_interpreters():for i in interpreters.list_all():if i.id == 0:  # maincontinuetry: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 threadingdef run_in_thread():t = threading.Thread(target=interpreters.create)print(t)t.start()print(t)t.join()print(t)run_in_thread()
run_in_thread()# <Thread(Thread-1 (create), initial)>
# <Thread(Thread-1 (create), started 139772371633728)>
# <Thread(Thread-1 (create), stopped 139772371633728)>
# <Thread(Thread-2 (create), initial)>
# <Thread(Thread-2 (create), started 139772371633728)>
# <Thread(Thread-2 (create), stopped 139772371633728)>

我们通过把interpreters.create函数传递给Thread,它会自动在线程内部生成新的子解释器。

我们也可以结合这两种方法,并将辅助函数传递给threading.Thread:

import timedef run_in_thread():interp = interpreters.create(isolated=True)t = threading.Thread(target=_run_output, args=(interp, dedent("""import _xxsubinterpreters as _interpreterscur = _interpreters.get_current()import timetime.sleep(2)# Can't print from here, won't bubble-up to main interpreterassert 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 complete
cleanup_interpreters()

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

Channels

如果我们深入研究CPython测试模块,我们还会发现有 RecvChannel 和 SendChannel 类的实现,它们类似于 Golang 中的通道(Channel)。要使用它们:

# https://github.com/python/cpython/blob/
#   15665d896bae9c3d8b60bd7210ac1b7dc533b093/Lib/test/test_interpreters.py#L583
r, s = interpreters.create_channel()print(f"Channel: {r}, {s}")
# Channel: RecvChannel(id=0), SendChannel(id=0)orig = b'spam'
s.send_nowait(orig)
obj = r.recv()
print(f"Received: {obj}")
# Received: b'spam'cleanup_interpreters()
# Need clean up, otherwise:# free(): invalid pointer
# Aborted (core dumped)

这个例子展示了如何创建一个带有receiver(r)和sender(s)端的通道。我们可以使用send_nowait将数据传递给发送方,并使用recv函数在另一侧读取它。这个通道实际上只是另一个子解释器 - 所以与之前一样 - 我们需要在完成后进行清理。

深入挖掘

最后,如果我们想要干扰或调整在C代码中设置的子解释器选项,那么可以使用test.support模块中的代码,具体来说就是run_in_subinterp_with_config:

import test.supportdef run_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"""from test.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)

这个函数是一个Python API,用于调用C函数。它提供了一些子解释器选项,如own_gil,指定子解释器是否应该拥有自己的GIL。

d93179f51ebf8de4d7ade911293e92bd.png

总结

话虽如此——也正如你所看到的,API调用并不简单,除非你已具备C语言专业知识,并且又迫切想要使用字解释器,否则建议还是等待Python 3.13的发布。或者您可以尝试extrainterpreters项目,该项目提供更友好的Python API以便使用子解释器。

推荐阅读:

▶张勇发全员信:阿里云将分拆上市;ChatGPT官方iOS应用上线,支持中文语音;Bun 0.6发布|极客头条

▶教授误用 ChatGPT 来论文“查重”,学生:不仅挂科了,我差点拿不了毕业证!

▶ChatGPT App 来了!

55b2690fa2162791a4780e5e99a0c6fa.jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/42305.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Circular lollipop | 哇咔咔!!!环形棒棒糖图好吃又好玩!~

1写在前面 今天不想废话了&#xff0c;直接看图吧。&#x1f447; 复现代码step by step&#xff0c;自己看吧。&#x1f92a; 2用到的包 rm(list ls())library(tidyverse)library(ggtext)library(patchwork) 3示例数据 df_pw <- read.csv("./passwords.csv",row…

最新突破!天然产物首次实现全合成,轰动整个化学界

01 科研背景 生物医学是综合医学、生命科学和生物学的理论和方法而发展起来的前沿交叉学科,基本任务是运用生物学及工程技术手段研究和解决生命科学&#xff0c;特别是医学中的有关问题。机器学习技术能利用复杂的算法在大规模、异质性数据集中进行运行,在生物医学方面、人类…

基于人工智能与机器学习等多种方法的研究内容

导语 生物医学是综合医学、生命科学和生物学的理论和方法而发展起来的前沿交叉学科,基本任务是运用生物学及工程技术手段研究和解决生命科学&#xff0c;特别是医学中的有关问题。机器学习技术能利用复杂的算法在大规模、异质性数据集中进行运行,在生物医学方面、人类基因组项…

《简说基因》公众号编辑招募公告

【简说基因】免费交流群&#xff0c;资料领取/学习交流 生物信息学是什么&#xff1f;看一看 ChatGPT 的回答&#xff1a; 生物信息学是一个跨学科领域&#xff0c;结合了生物学和计算机科学的原理和技术&#xff0c;旨在研究和解释生物学数据。它利用计算机算法和统计方法来分…

挖呀挖!AutoDock 分子对接极简实践方法

挖呀挖&#xff01;AutoDock 分子对接极简实践方法&#x1f44b; 哈哈哈 开心&#x1f606; 在小小的花园里面挖呀挖呀挖 &#x1f331; 种下一颗小小的种子&#x1fad8; 开出一朵小小的花&#x1f338; 在大大的花园里面挖呀挖呀挖&#x1f331; 种下一颗大大的种子&#x1f…

【TOP生物信息】CNS图表复现,单细胞marker基因展示的另一种方式——蜂巢图

扫码关注下方公粽号&#xff0c;回复推文合集&#xff0c;获取400页单细胞学习资源&#xff01; 本文共计1359字&#xff0c;阅读大约需要4分钟。 Sten Linnarsson大神的单细胞绘图堪称极致美学&#xff0c;在这里&#xff0c;小编选择了发表在nature上展示marker基因的绘图进…

【Vue2从入门到精通】深入浅出,带你彻底搞懂Vue2组件通信的9种方式

文章目录 人工智能福利文章Vue组件间通信分类1.props / $emit1.1 父组件向子组件传值1.2 子组件向父组件传值 2.$parent / $children3.ref / $refs3.1 ref作用于组件3.2 ref作用于Html标签3.3 $nextTick() 4.EventBus &#xff08;$emit / $on&#xff09;4.1 初始化4.2 发送事…

三国鼎立:ChatGPT、百度文心一言、微软 New Bing

推荐国内可访问chatGPT: chatGTP --------------------------------------------------------------------------------------------------------------- ChatGTP&#xff1a;更像是三国中魏国&#xff0c;地大物博用户多。 百度文心一言&#xff1a;更像是三国中的蜀国&…

在Mac中设置Ctrl+C/V进行复制/粘贴

在win环境转到mac环境,感觉最大的就是这个⌘键了,看了很多帖子感觉都是片面的 进行系统偏好设置 > 键盘 > 快捷键 > App快捷键点击如图加号 这一步很重要,比如你要修改访达的复制粘贴,那就代打开访达 根据访达的编辑一栏自定义修改 菜单标题就是 全选 键盘快捷键自…

如何在Mac上复制和粘贴

如果您是具有Windows PC背景的新Mac用户&#xff0c;则可能想知道如何在macOS中使用典型的复制和粘贴命令。 在Windows中&#xff0c;“复制”和“粘贴”键组合分别为Control-C和Control-V。在Mac上&#xff0c;这非常相似–您要做的就是使用Command&#xff08;⌘&#xff09;…

mac截图复制到剪贴板_如何一次将多个内容复制到Mac的剪贴板中

mac截图复制到剪贴板 You know that thing where you copy something important, forget to paste it anywhere, then copy something else? It sucks, because the important thing you copied first is gone. 您知道要复制重要内容的东西&#xff0c;忘记将其粘贴到任何地方…

Mac复制改为拷贝

在默认情况下&#xff0c;mac电脑的复制为复制粘贴&#xff0c;即会生成一个副本文件&#xff0c;并没有达到Win下复制功效。 不知你是否遇到过&#xff0c;下面贴图介绍如何调整为真正的复制功能。 1.打开 系统偏好设置 2.选择 键盘 3.快捷键-App快捷键 然后删除 复制 快捷键…

Mac mini 使用普通键盘设置 Windows 的粘贴、复制、剪切习惯

问题描述 通常我们会习惯性用左手小拇指按最左下角的 ctrl 键作为功能前缀键&#xff0c;进行复制、粘贴、剪切等操作&#xff0c;但将普通键盘接到 macOS 设备上&#xff0c;会将 windows 徽标键视为 command 键&#xff0c;此时习惯了 Windows 系统的朋友进行复制、粘贴、剪…

mac item2 复制会话session

按照如下图红框内设置 Command中输入ssh 常登陆的ip&#xff08;如果不配置这一步则复制session时还要自己输入ssh命令&#xff0c;只是不需要输入密码而已&#xff09; 在自己的mac终端, vim ~/.ssh/config 添加 host *ControlMaster autoControlPath ~/.ssh/master-%r%h:…

WindowsMac剪贴板如何存储多条复制记录

Windows版 1.第一步点击电脑左下方“开始菜单”按钮。 2..点击 “设置”按钮。 3.打开设置之后点击"系统"。 4.然后下拉找到“剪贴板”。 5.选中剪贴板&#xff0c;然后在右侧即可开启剪贴板历史记录功能。 按“WindowsV”组合键键即可打开剪贴板。复制或者剪切过的…

五个最好的复制/粘贴的Mac App应用

如果你刚用Mac&#xff0c;之前是用Windows电脑的&#xff0c;第一个问题就会发现&#xff0c;Mac上的复制/粘贴找不到了。如果你也有同样的问题&#xff0c;或者你在找Mac上有没有好用的剪贴板管理工具&#xff0c;可以在下面的文章中找到答案。 Mac上怎样优雅的复制/粘贴 c…

Mac简单易用的复制软件——“TouchCopy”

TouchCopy是Mac平台上一款简单易用的复制软件&#xff0c;可以轻松的将你的照片、视频、通讯录、文档、日历复制到pc端或者是磁盘里。有需要的朋友欢迎前来macdown下载体验&#xff01; 功能 1、将音乐从iPhone / iPod / iPad传输到iTunes将音乐从iPod或iOS设备复制到PC或Mac硬…

苹果Mac电脑的复制粘贴不能用了

复制 (⌘ C) 和粘贴 (⌘ V) 是使用Mac电脑过程中遇到频率非常高的快捷组合键&#xff0c;突然你发现它们不能使用了&#xff0c;会非常崩溃。macw小编今天为大家带来详细解决办法&#xff0c;让您的复制粘贴重新启用起来&#xff01; 为什么 Mac 剪贴板不起作用 我们在 Mac…

Mac下iterm2 克隆会话功能

因为每次需要先登录跳转机&#xff0c;再登录开发机&#xff0c;输入两次密码&#xff0c;过于繁琐&#xff0c;所以上网找了iterm下克隆会话功能。 网上说command下的command也要配置&#xff0c;亲测不用&#xff0c;只选中下面的reuse previous sessions’s directory就行。…

花启宝是什么软件,来聊一聊“花启宝”的详情

很多人在问我&#xff0c;你在做的软件是什么&#xff1f;我本人也就是通过它慢慢学习成长起来的。还是一个网友推荐的&#xff0c;现在他对我来说算是大佬了&#xff0c;已经单飞了&#xff0c;好久都没有联系了&#xff0c;呵呵&#xff0c;真是人往高处走&#xff0c;水往低…