AnyMath 文档

世界年龄机制

注意世界时代是一个先进的概念。 对于绝大多数Julia用户来说,世界年龄机制在后台无形地运行。 此文档适用于可能遇到与世界年龄相关的问题或错误消息的少数用户。

兼容性

Julia1.12在Julia1.12之前,世界年龄机制不适用于全局绑定表的更改。 本章中的文档特定于Julia1.12+。

警告本手册章使用内部函数来自省世界时代和运行时数据结构作为解释性帮助。 一般来说,除非另有说明,否则世界年龄机制不是一个稳定的接口,应该通过稳定的Api在包中进行交互(例如 invokelatest,invokelatest)只。 特别是,不要假设世界年龄总是整数,或者它们具有线性顺序。

一般世界年龄

"世界年龄计数器"是一个单调递增的计数器,每次对全局方法表或全局绑定表的更改都会递增(例如通过方法定义,类型定义, 进口/使用 声明,创建(类型化)全局变量或定义常量)。

全局世界年龄计数器的当前值可以使用(内部)函数检索 基地。get_world_counter.

julia> Base.get_world_counter()
0x0000000000009632

julia> const x = 1

julia> Base.get_world_counter()
0x0000000000009633

此外,每 任务存储一个本地世界年龄,该年龄确定对全局绑定和方法表的哪些修改当前对正在运行的任务可见。 运行任务的世界年龄永远不会超过全球世界年龄计数器,但可能会在其背后任意运行。 一般来说,术语"当前世界年龄"是指当前正在运行的任务的本地世界年龄。 可以使用(内部)函数检索当前世界年龄 基地。tls_world_age

julia> function f end
f (generic function with 0 methods)

julia> begin
           @show (Int(Base.get_world_counter()), Int(Base.tls_world_age()))
           Core.eval(@__MODULE__, :(f() = 1))
           @show (Int(Base.get_world_counter()), Int(Base.tls_world_age()))
           f()
       end
(Int(Base.get_world_counter()), Int(Base.tls_world_age())) = (38452, 38452)
(Int(Base.get_world_counter()), Int(Base.tls_world_age())) = (38453, 38452)
ERROR: MethodError: no method matching f()
The applicable method may be too new: running in current world age 38452, while global world is 38453.

Closest candidates are:
  f() (method too new to be called from this world context.)
   @ Main REPL[2]:3

Stacktrace:
 [1] top-level scope
   @ REPL[2]:5

julia> (f(), Int(Base.tls_world_age()))
(1, 38453)

这里方法的定义 f 提出了全球世界计数器,但目前的世界时代并没有改变。 因此,定义 f 在当前执行的任务和a中不可见 方法;方法结果。

注意方法错误打印提供了以下附加信息 f() 在一个新的世界时代可用。 此信息是由错误显示添加的,而不是抛出 方法;方法. 被抛出的 方法;方法 是否匹配的定义是相同的 f() 存在于一个更新的世界时代。

但是,请注意,定义 f() 随后在下一个REPL提示时可用,因为当前任务的世界年龄已经提高。 一般来说,某些语法结构(特别是大多数定义)将当前任务的世界年龄提高到最新的全球世界年龄,从而使所有更改(从当前任务和任何同时执行的其他任务)都可见。 以下陈述提高了当前的世界年龄:

  1. 显式调用 核心。@最新世界

  2. 每个顶级声明的开始

  3. 每个REPL提示的开始

  4. 任何类型或结构定义

  5. 任何方法定义

  6. 任何常量声明

  7. 任何全局变量声明(但不是全局变量赋值)

  8. 任何 使用, 进口, 出口公众人士 声明

  9. 某些其他宏,如 @eval(取决于宏实现)

但是请注意,当前任务的世界年龄可能只会在顶级永久递增。 作为一般规则,在非顶级作用域中使用上述任何语句都是语法错误:

julia> f() = Core.@latestworld
ERROR: syntax: World age increment not at top level
Stacktrace:
 [1] top-level scope
   @ REPL[5]:1

当它不是(例如对于 @eval),世界年龄副作用被忽略。

作为这些规则的结果,Julia可能会假设世界年龄在普通函数的执行中不会发生变化。

function my_function()
    before = Base.tls_world_age()
    # Any arbitrary code
    after = Base.tls_world_age()
    @assert before === after # always true
end

这是关键的不变量,它允许Julia根据其全局数据结构的当前状态进行优化,同时仍然具有明确定义的更改这些数据结构的能力。

暂时提高世界年龄 invokelatest,invokelatest

如上所述,不可能在a的剩余时间内永久提高世界年龄。 任务执行,除非任务正在执行顶级语句。 但是,可以使用以下方法暂时改变世界年龄 invokelatest,invokelatest:

julia> function f end
f (generic function with 0 methods)

julia> begin
           Core.eval(@__MODULE__, :(f() = 1))
           invokelatest(f)
       end
1

invokelatest,invokelatest 将暂时将当前任务的世界年龄提高到最新的全球世界年龄(在进入 invokelatest,invokelatest)并执行所提供的功能。 请注意,世界年龄将在退出时恢复到其先前的值 invokelatest,invokelatest.

世界时代与const结构重新定义

上面描述的方法重新定义的语义也适用于常量的重新定义:

julia> const x = 1
1

julia> get_const() = x
get_const (generic function with 1 method)

julia> begin
           @show get_const()
           Core.eval(@__MODULE__, :(const x = 2))
           @show get_const()
           Core.@latestworld
           @show get_const()
       end
get_const() = 1
get_const() = 1
get_const() = 2
2

但是,为了避免疑问,它们不适用于对全局变量的普通赋值,这会立即变得可见:

julia> global y = 1
1

julia> get_global() = y
get_global (generic function with 1 method)

julia> begin
           @show get_global()
           Core.eval(@__MODULE__, :(y = 2))
           @show get_global()
       end
get_global() = 1
get_global() = 2
2

常量重新分配的一个特殊情况是结构类型的重新定义:

julia> struct MyStruct
           x::Int
       end

julia> const one_field = MyStruct(1)
MyStruct(1)

julia> struct MyStruct
           x::Int
           y::Float64
       end

julia> const two_field = MyStruct(1, 2.0)
MyStruct(1, 2.0)

julia> one_field
@world(MyStruct, 38452:38455)(1)

julia> two_field
MyStruct(1, 2.0)

内部的两个定义 我的结构 是完全独立的类型。 然而,在新的 我的结构 类型被定义,不再有任何默认绑定的原始定义 我的结构. 为方便查阅这些类别,特别 @世界宏可以用来访问一个名字在以前的世界中的含义。 然而,这个工具只用于内省,特别是要注意的是,世界年龄数字在预编译过程中并不稳定,一般应该被不透明地对待。

绑定分区内省

在某些情况下,反思系统对任何特定世界时代约束意味着什么的理解可能会有所帮助。 的默认显示打印 核心。绑定 提供有用的摘要(例如 我的结构 从上面的例子):

julia> convert(Core.Binding, GlobalRef(@__MODULE__, :MyStruct))
Binding Main.MyStruct
   38456:∞ - constant binding to MyStruct
   38452:38455 - constant binding to @world(MyStruct, 38452:38455)
   38451:38451 - backdated constant binding to @world(MyStruct, 38452:38455)
   0:38450 - backdated constant binding to @world(MyStruct, 38452:38455)

世界年龄和 使用/进口

通过提供的绑定 使用进口 也通过世界年龄机制运作。 绑定解析是 进口使用 在当前世界时代可见的定义。 例如:

julia> module M1; const x = 1; export x; end

julia> module M2; const x = 2; export x; end

julia> using .M1

julia> x
1

julia> using .M2

julia> x
ERROR: UndefVarError: `x` not defined in `Main`
Hint: It looks like two or more modules export different bindings with this name, resulting in ambiguity. Try explicitly importing it from a particular module, or qualifying the name with the module it should come from.

julia> convert(Core.Binding, GlobalRef(@__MODULE__, :x))
Binding Main.x
   38458:∞ - ambiguous binding - guard entry
   38457:38457 - implicit `using` resolved to constant 1

世界年龄捕捉

某些语言功能捕获当前任务的世界年龄。 也许其中最常见的是创建新任务。 新创建的任务将在创建时继承创建任务的本地世界年龄,并将保留所述世界年龄(除非明确提出),即使原始任务提高了其世界年龄:

julia> const x = 1

julia> t = @task (wait(); println("Running now"); x);

朱莉娅>const x=2

朱莉娅>时间表(t);
现在跑步

朱莉娅>x
2

朱莉娅>fetch(t)
1

除了任务之外,不透明的闭包也捕捉到了它们在创造时的世界年龄。 见 基地。实验性的。@不透明.

@world(sym, world)

解决绑定 西姆 在世界上 世界. 见 invoke_in_世界用于在固定世界中运行任意代码。 世界 可能是 单位范围,在这种情况下,宏将出错,除非绑定是有效的,并且在整个世界范围内具有相同的值。

作为一个特例,世界 总是指最新的世界,即使那个世界比当前运行的世界更新。

@世界 宏主要用于打印在当前世界中不再可用的绑定。

*例子*

julia> struct Foo; a::Int; end
Foo

julia> fold = Foo(1)

julia> Int(Base.get_world_counter())
26866

julia> struct Foo; a::Int; b::Int end
Foo

julia> fold
@world(Foo, 26866)(1)
兼容性

Julia1.12此功能至少需要Julia1.12。

get_world_counter()

返回当前最大世界年龄计数器。 这个计数器是全局的,单调递增的。

tls_world_age()

让世界回归 current_task()正在执行内。

invoke_in_world(world, f, args...; kwargs...)

打电话 f(args...;夸格斯。..) 在一个固定的世界时代, 世界.

这对于在用户的Julia会话中运行的基础结构非常有用,该会话不是用户程序的一部分。 例如,与REPL,编辑器支持库等相关的东西。 在这些情况下,防止不需要的方法失效和重新编译延迟,并防止用户错误地破坏支持基础结构是很有用的。

可以使用以下方法查询全球世界年龄 基地。get_world_counter()并存储以供以后在当前Julia会话的生存期内使用,或者在序列化和重新加载系统映像时使用。

技术上讲, invoke_in_世界 将阻止由调用的任何函数 f 从在他们的Julia会话期间被用户扩展。 即泛型函数方法表 f (以及它调用的任何函数)将被冻结,因为它们存在于给定的 世界 年龄。 从某种意义上说,这就像是相反的 invokelatest,invokelatest.

请注意,存储在预编译中获得的世界年龄以供以后使用是无效的。 这是因为预编译生成了一个"并行宇宙",其中世界年龄指的是与主Julia会话无关的系统状态。

@opaque ([type, ]args...) -> body

将给定闭包标记为"不透明"。 不透明的闭包捕捉了它们创造的世界时代(而不是它们的调用)。 这允许对捕获列表进行更积极的优化,但如果它们的创建不是静态可见的,则可以在调用站点内联不透明闭包。

参数元组类型(类型)可以选择指定,以更灵活的方式指定允许的参数类型。 特别是,即使函数是可变参数,参数类型也可以是固定长度。

警告此接口是实验性的,如有更改或删除,恕不另行通知.