”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 比较 Python 和 ArkScript 异步模型

比较 Python 和 ArkScript 异步模型

发布于2024-11-08
浏览:192

Comparing Python and ArkScript asynchronous models

Python 最近受到了很多关注。计划于今年 10 月发布的 3.13 版本将开始删除 GIL 的艰巨工作。对于想要尝试(几乎)无 GIL Python 的好奇用户来说,预发行版已经发布。

所有这些炒作让我用我自己的语言 ArkScript 进行挖掘,因为我过去也有一个全局 VM 锁(在 2020 年的 3.0.12 版本中添加,在 2022 年的 3.1.3 中删除),以比较事物并迫使我更深入地研究 Python GIL 的方式和原因。

定义

  1. 首先,让我们定义什么是 GIL(全局解释器锁):

全局解释器锁(GIL)是计算机语言解释器中使用的一种机制,用于同步线程的执行,以便只有一个本机线程(每个进程)可以在同一时间执行基本操作(例如内存分配和引用计数)。时间。

维基百科 — 全局解释器锁

  1. 并发是指两个或多个任务可以在重叠的时间段内启动、运行和完成,但这并不意味着它们将同时运行。

  2. 并行性 是指任务同时运行,例如在多核处理器上。

如需深入解释,请查看 Stack Overflow 的答案。

Python 的 GIL

GIL 可以提高单线程程序的速度,因为您不必获取和释放所有数据结构上的锁:整个解释器都被锁定,因此默认情况下您是安全的。

然而,由于每个解释器有一个 GIL,这限制了并行性:您需要在单独的进程中生成一个全新的解释器(使用多处理模块而不是线程)才能使用多个核心!这比仅仅生成一个新线程的成本更高,因为您现在必须担心进程间通信,这会增加不可忽略的开销(有关基准测试,请参阅 GeekPython - Python 3.13 中的 GIL 成为可选)。

它如何影响Python的异步?

就 Python 而言,这取决于主要实现 CPython 没有线程安全的内存管理。如果没有 GIL,以下情况将产生竞争条件:

  1. 创建共享变量 count = 5
  2. 线程 1:计数 *= 2
  3. 线程 2:计数 = 1

如果线程 1 首先运行,则计数将为 11(计数 * 2 = 10,则计数 1 = 11)。

如果 线程 2 首先运行,则计数将为 12(计数 1 = 6,则计数 * 2 = 12)。

执行顺序很重要,但更糟糕的情况可能会发生:如果两个线程同时读取 count,一个线程将擦除另一个线程的结果,并且 count 将是 10 或 6!

总体而言,在一般情况下,拥有 GIL 可以使 (CPython) 实现更轻松、更快:

  • 在单线程情况下更快(不需要为每个操作获取/释放锁)
  • 在 IO 绑定程序的多线程情况下更快(因为这些发生在 GIL 之外)
  • 对于在 C 中执行计算密集型工作的 CPU 密集型程序来说,在多线程情况下速度更快(因为 GIL 在调用 C 代码之前被释放)

它还使包装 C 库变得更容易,因为通过 GIL 保证了线程安全。

缺点是您的代码是异步,如并发,但不是并行

[!笔记]
Python 3.13 正在删除 GIL!

PEP 703 添加了构建配置 --disable-gil,以便在安装 Python 3.13 后,您可以从多线程程序的性能改进中受益。

Python 异步/等待模型

在Python中,函数必须采用颜色:它们要么是“正常”,要么是“异步”。这在实践中意味着什么?

>>> def foo(call_me):
...     print(call_me())
... 
>>> async def a_bar():
...     return 5
... 
>>> def bar():
...     return 6
... 
>>> foo(a_bar)
:2: RuntimeWarning: coroutine 'a_bar' was never awaited
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
>>> foo(bar)
6

因为异步函数不会立即返回值,而是调用协程,所以我们不能在任何地方都将它们用作回调,除非我们调用的函数被设计为采用异步回调。

我们得到了函数的层次结构,因为“普通”函数需要异步才能使用await关键字,需要调用异步函数:

         can call
normal -----------> normal

         can call
async - -----------> normal
       |
       .-----------> async                    

除了信任调用者之外,没有办法知道回调是否是异步的(除非您尝试首先在 try/ except 块中调用它来检查异常,但这很丑陋)。

ArkScript并行性

一开始,ArkScript 使用全局 VM 锁(类似于 Python 的 GIL),因为 http.arkm 模块(用于创建 HTTP 服务器)是多线程的,它通过修改变量来改变其状态,从而导致 ArkScript 的 VM 出现问题并在多个线程上调用函数。

然后在 2021 年,我开始研究一种新模型来处理虚拟机状态,以便我们可以轻松并行化它,并写了一篇关于它的文章。后来在 2021 年底实现,全局虚拟机锁被移除。

ArkScript 异步/等待

ArkScript 不会为异步函数分配颜色,因为它们在语言中不存在:你要么有一个函数,要么有一个闭包,两者都可以相互调用而无需任何额外的语法(闭包是一个穷人对象,在这种语言中:保持可变状态的函数)。

任何函数都可以在调用站点上异步(而不是声明):

(let foo (fun (a b c)
    (  a b c)))

(print (foo 1 2 3))  # 6

(let future (async foo 1 2 3))
(print future)          # UserType
(print (await future))  # 6
(print (await future))  # nil

使用 async 内置函数,我们在后台生成一个 std::future (利用 std::async 和线程)来运行给定一组参数的函数。然后我们可以调用await(另一个内置函数)并在需要时获取结果,这将阻塞当前VM线程直到函数返回。

因此,可以从任何函数和任何线程等待。

特殊性

所有这一切都是可能的,因为我们有一个虚拟机,它在 Ark::internal::ExecutionContext 内包含的状态上运行,该状态与单个线程绑定。 VM 在线程之间共享,而不是在上下文之间共享!

        .---> thread 0, context 0
        |            ^
VM  thread 1, context 1              

当使用异步创建future时,我们是:

  1. 将所有参数复制到新上下文,
  2. 创建一个全新的堆栈和范围,
  3. 最终创建一个单独的线程。

这禁止线程之间任何类型的同步,因为 ArkScript 不会公开引用或任何可以共享的锁(这样做是为了简单起见,因为该语言的目标是有点简约但仍然可用)。

然而,这种方法并不比 Python 更好(也不更差),因为我们每次调用都会创建一个新线程,并且每个 CPU 的线程数量是有限的,这有点昂贵。幸运的是,我不认为这是需要解决的问题,因为永远不应该同时创建数百或数千个线程,也不应同时调用数百或数千个异步 Python 函数:两者都会导致程序速度大幅减慢。

在第一种情况下,这会减慢您的进程(甚至是计算机),因为操作系统正在努力为每个线程提供时间;在第二种情况下,Python 的调度程序必须在所有协程之间进行处理。

[!笔记]
开箱即用,ArkScript 不提供线程同步机制,但即使我们将 UserType (它是 type-erased C 对象之上的包装器)传递给函数,底层对象也不是已复制。

通过一些仔细的编码,可以使用 UserType 构造创建一个锁,这将允许线程之间的同步。

(let lock (module:createLock))
(let foo (fun (lock i) {
  (lock true)
  (print (str:format "hello {}" i))
  (lock false) }))
(async foo lock 1)
(async foo lock 2)

结论

ArkScript 和 Python 使用两种截然不同的 async/await:第一种需要在调用站点使用 async 并生成一个具有自己上下文的新线程,而后者则要求程序员将函数标记为 async能够使用await,并且这些异步函数是协程,与解释器在同一线程中运行。

来源

  1. Stack Exchange — 为什么 Python 是用 GIL 编写的?
  2. Python Wiki — GlobalInterpreterLock
  3. stuffwithstuff - 你的函数是什么颜色?

源自 lexp.lt

版本声明 本文转载于:https://dev.to/lexplt/comparing-python-and-arkscript-asynchronous-models-3l60?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • AdaBoost - 集成方法,分类:监督机器学习
    AdaBoost - 集成方法,分类:监督机器学习
    Boosting Definition and Purpose Boosting is an ensemble learning technique used in machine learning to improve the accuracy of models...
    编程 发布于2024-11-09
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-11-09
  • 重新学习CS基础知识——实现队列
    重新学习CS基础知识——实现队列
    你曾经站在队列中吗,队列数据结构也做同样的事情。当你想在你最喜欢的自助餐厅点餐时,你站在队伍的最后,然后你就可以继续排队并离开。 CS 中的队列数据结构执行相同的功能。队列数据结构是先进先出的数据结构。队列数据结构可以使用两个基本函数 Enqueue 和 Dequeue 来构建,这两个函数基本上是...
    编程 发布于2024-11-09
  • 为 Angular 18 设置 linter 和 IDE
    为 Angular 18 设置 linter 和 IDE
    将 eslint、prettier、env 添加到应用程序中。 遗憾的是,Angular 默认情况下不会自行生成这一切。更改原理图可以提高数千个 Angular 项目的质量。 设置 eslint 9 连接 eslint: yarn ng add @angular-eslint/sch...
    编程 发布于2024-11-09
  • 使用 JavaScript 进行网页抓取和代理设置的初学者指南
    使用 JavaScript 进行网页抓取和代理设置的初学者指南
    使用JavaScript代码模拟用户操作,获取所需信息。包括模拟用户打开网页、点击链接、输入关键字等操作,并从网页中提取所需信息。 Javascript网页抓取的核心原理 使用JavaScript代码模拟用户操作来获取所需信息。包括模拟用户打开网页、点击链接、输入关键字等操作,并从网...
    编程 发布于2024-11-09
  • 在 Android 上运行 Llama:使用 Ollama 的分步指南
    在 Android 上运行 Llama:使用 Ollama 的分步指南
    Llama 3.2 最近在 Meta 开发者大会上推出,展示了令人印象深刻的多模式功能以及针对使用高通和联发科技硬件的移动设备进行优化的版本。这一突破使开发人员能够在移动设备上运行 Llama 3.2 等强大的 AI 模型,为更高效、私密和响应迅速的 AI 应用程序铺平道路。 Meta 发布了 Ll...
    编程 发布于2024-11-09
  • 如何在 Python 中格式化字符串以将它们对齐直列?
    如何在 Python 中格式化字符串以将它们对齐直列?
    以固定宽度打印字符串打印字符串时,将它们对齐成直列可以增强可读性。在 Python 中使用 format 或 f-string 提供了实现此目的的便捷方法。使用 str.format()str.format() 提供了一种简单的填充方法字符串。其语法包括占位符 {},后跟格式化表达式。对于左对齐,请...
    编程 发布于2024-11-09
  • 为什么微服务比单体架构重要
    为什么微服务比单体架构重要
    在当今快节奏的技术环境中,企业需要可扩展且灵活的解决方案来快速适应不断变化的需求。与传统的整体方法相比,这就是微服务架构的亮点。 1.什么是单体架构? 单体架构是一个单一的、统一的系统,其中所有组件都是互连和相互依赖的。这意味着对系统的任何更改或更新都需要重新构建和重新部署整个应用程...
    编程 发布于2024-11-09
  • 如何在 PHP 中访问对象属性:了解语法和错误解决方案
    如何在 PHP 中访问对象属性:了解语法和错误解决方案
    理解 PHP 对象属性访问在 PHP 中,访问对象属性对于处理复杂的数据结构至关重要。属性保存与对象关联的信息,使我们能够管理和操作该数据。访问对象属性有两种常用语法:1。 $property1此语法直接通过名称访问特定属性。它用于分配或检索各个属性的值。但是,这种方法要求您提前知道确切的属性名称。...
    编程 发布于2024-11-09
  • PDO如何防止SQL注入并替换转义单引号?
    PDO如何防止SQL注入并替换转义单引号?
    PDO防止SQL注入的方法如果你已经从mysql库过渡到PDO,你可能想知道如何替换real_escape_string用于转义发往数据库的字符串中的单引号的函数。虽然向每个字符串添加斜杠可能看起来很麻烦,但 PDO 提供了一种更有效的替代方案。PDO 准备的强大功能为了防止 SQL 注入,PDO ...
    编程 发布于2024-11-09
  • 通过“项目:使用互斥体同步多线程打印”课程释放您的编码潜力
    通过“项目:使用互斥体同步多线程打印”课程释放您的编码潜力
    您准备好深入多线程编程的世界并学习如何使用互斥体来同步字符串的打印了吗? LabEx 提供的项目:使用互斥体同步多线程打印课程就是您的最佳选择。 在这个基于项目的综合课程中,您将踏上了解互斥体在协调多线程执行方面的重要性的旅程。您将首先修改现有的“混沌打字机”程序,确保字符串以正确的顺序打印,而不会...
    编程 发布于2024-11-09
  • 为什么我在 MySQL 中收到“\'create_date\'时间戳字段的默认值无效”错误?
    为什么我在 MySQL 中收到“\'create_date\'时间戳字段的默认值无效”错误?
    “create_date”时间戳字段的默认值无效创建带有时间戳列的表并指定默认值“0000-”时00-00 00:00:00',可能会出现错误,指示“'create_date'的默认值无效”。这个错误是由 MySQL 的 SQL 模式 - NO_ZERO_DATE 引起的。根...
    编程 发布于2024-11-09
  • 尽管出现“页面已移动”错误,如何使用 cURL 检索页面内容?
    尽管出现“页面已移动”错误,如何使用 cURL 检索页面内容?
    使用 cURL 检索页面内容在此上下文中,您试图使用 cURL 抓取 Google 搜索结果页面的内容。尽管尝试设置用户代理和各种选项,但您仍无法成功检索页面内容。重定向或“页面移动”错误继续困扰着您。据信该问题可能源于查询字符串中特殊字符的编码。为了缓解这种情况,需要更改 PHP 代码。方法如下:...
    编程 发布于2024-11-09
  • 如何使用 JPA 和 Hibernate 以 UTC 格式存储日期/时间?
    如何使用 JPA 和 Hibernate 以 UTC 格式存储日期/时间?
    使用 JPA 和 Hibernate 在 UTC 时区存储日期/时间在 JPA/ 中处理日期和时间时担心时区差异休眠应用程序?本文探讨了如何在 UTC (GMT) 时区有效存储和检索时态数据,确保跨不同时区进行一致且准确的处理。考虑下面带注释的 JPA 实体:public class Event {...
    编程 发布于2024-11-09
  • 如何使用 CSS 创建动态扩展的文本输入字段?
    如何使用 CSS 创建动态扩展的文本输入字段?
    通过 CSS 增强文本输入响应能力制作 Web 表单时,控制文本输入字段的大小至关重要。 CSS 提供了一种简单的方法来定义其初始尺寸。但是,如果您希望输入随着用户键入而动态扩展并达到最大宽度,该怎么办?本文深入研究了仅 CSS 和基于 HTML 的技术来实现此行为。CSS 和内容可编辑利用 CSS...
    编程 发布于2024-11-09

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3