」工欲善其事,必先利其器。「—孔子《論語.錄靈公》
首頁 > 程式設計 > 比較 Python 和 ArkScript 非同步模型

比較 Python 和 ArkScript 非同步模型

發佈於2024-11-08
瀏覽:724

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]刪除
最新教學 更多>

免責聲明: 提供的所有資源部分來自互聯網,如果有侵犯您的版權或其他權益,請說明詳細緣由並提供版權或權益證明然後發到郵箱:[email protected] 我們會在第一時間內為您處理。

Copyright© 2022 湘ICP备2022001581号-3