Task α 是异步计算的原语。
它表示一个最终会解析为 α 类型值的计算,该计算可能在另一线程上进行。这类似于 Scala 中的 Future、Javascript 中的 Promise 和 Rust 中的 JoinHandle。
任务在运行时中使用覆写的表示。
任务是编写多线程代码的基本原语。
Task α 表示一个会在某一时刻兑现为 α 类型值的计算;该计算可以在另一个线程上执行。
任务兑现后即可读取其值;若在兑现前尝试取得其值,当前线程会阻塞,直至任务兑现。
任务类似于 JavaScript 中的承诺、Rust 中的 JoinHandle 以及 Scala 中的 Future。
任务既可以执行纯计算,也可以执行 IO 动作。
纯任务的 API 类似于悬式的 API:Task.spawn 从 Unit → α 函数创建 Task α,而 Task.get 等待函数值计算完毕后将其返回。
该值会被缓存,后续请求无须重新计算。
关键区别在于计算发生的时机:悬式的值只有在被强制求值时才计算,而任务会伺机在另一线程中执行。
IO 中的任务使用 IO.asTask 创建。
类似地,BaseIO.asTask 与 EIO.asTask 用于在其他 IO 单子中创建任务。
这些任务可能产生副作用,也可以与其他任务通信。
当任务的最后一个引用被丢弃时,该任务会被取消。
使用 Task.spawn 创建的纯任务会在取消时终止。
使用 IO.asTask、EIO.asTask 或 BaseIO.asTask 生成的任务会继续执行,必须使用 IO.checkCanceled 显式检查是否已取消。
可以使用 IO.cancel 显式取消任务。
Lean 运行时维护一个用于运行任务的线程池。
若设置了环境变量 LEAN_NUM_THREADS,线程池大小由它决定;否则由当前机器的逻辑处理器数量决定。
线程池大小并非硬性上限;在某些情况下,为避免死锁可以超出该大小。
默认情况下,这些线程用于运行任务;每个任务都有一个优先级(Task.Priority),高优先级任务先于低优先级任务执行。
也可以用足够高的优先级生成任务,从而为其分配专用线程。
Task α 是异步计算的原语。
它表示一个最终会解析为 α 类型值的计算,该计算可能在另一线程上进行。这类似于 Scala 中的 Future、Javascript 中的 Promise 和 Rust 中的 JoinHandle。
任务在运行时中使用覆写的表示。
纯任务通常应使用 Task.spawn 创建;Task.pure 则表示一个已经兑现为所给值的任务。
非纯任务由某个 asTask 动作创建。
纯任务可以在 IO 单子族之外创建。
当其最后一个引用被丢弃时,它们会终止。
Task.spawn.{u} {α : Type u} (fn : Unit → α) (prio : Task.Priority := Task.Priority.default) : Task αTask.spawn.{u} {α : Type u} (fn : Unit → α) (prio : Task.Priority := Task.Priority.default) : Task α
spawn fn : Task α 构造并立即启动一个新任务,以异步求值函数 fn () : α。
如果提供了 prio,它就是该任务的优先级。
使用某个 asTask 函数生成带副作用的任务时,务必要真正执行所得的 IO 动作。
每次执行所得动作时都会生成一个任务;调用 asTask 时并不会生成任务。
即使不再有任何引用,非纯任务仍会继续运行,不过此时会发出取消请求。
也可以使用 IO.cancel 显式请求取消。
非纯任务必须使用 IO.checkCanceled 检查取消请求。
BaseIO.asTask {α : Type} (act : BaseIO α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task α)BaseIO.asTask {α : Type} (act : BaseIO α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task α)
以优先级 prio 在单独的 Task 中运行 act。
运行所得的 BaseIO 操作会立即启动任务。对 Task 的纯访问不会影响非纯操作 act。
与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
EIO.asTask {ε α : Type} (act : EIO ε α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task (Except ε α))EIO.asTask {ε α : Type} (act : EIO ε α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task (Except ε α))
以优先级 prio 在单独的 Task 中运行 act。由于 EIO ε 操作可能抛出 ε 类型的异常,任务结果是 Except ε α。
运行所得的 IO 操作会立即启动任务。对 Task 的纯访问不会影响非纯操作 act。
与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
IO.asTask {α : Type} (act : IO α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task (Except IO.Error α))IO.asTask {α : Type} (act : IO α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task (Except IO.Error α))
线程调度器使用任务优先级把任务分配给线程。
在 default 到 max 的优先级范围内,高优先级任务总是先于低优先级任务执行。
以 dedicated 优先级生成的任务会被分配各自的专用线程,不会与其他任务争用线程池中的线程。
所生成任务的最高常规优先级:8。
以高于 Task.Priority.max 的优先级生成任务并非错误,但会为该任务生成专用工作线程。这由 Task.Priority.dedicated 表示。常规优先级任务会放入线程池,并按优先级顺序处理。
表示任务应在专用线程上调度。
任何高于 Task.Priority.max 的优先级都会使任务立即在专用线程上调度。这对长时间运行和/或受 I/O 限制的任务尤其有用,因为为减少上下文切换,Lean 默认分配的非专用工作线程不会超过核心数。
阻塞当前线程,直到给定任务执行完毕,然后返回任务结果。如果当前线程自身正在执行一个(非专用)任务,则等待期间会临时将线程池的最大大小增加一,以确保进程不会因线程池资源耗尽而死锁。请注意,当前线程解除阻塞时,可能会暂时有超过所配置线程池大小的任务同时运行,直到足够多的任务执行完毕。
在可行的情况下,应优先使用 Task.map 和 Task.bind 来建立任务依赖,而不是使用 Task.get,因为它们不需要以这种方式临时扩展线程池。尤其是,在任务续体中以 (sync := true) 调用 Task.get 会引发恐慌,因为此时续体显然并不“廉价”,否则还可能发生死锁。应改为返回所等待的任务,并使用 Task.bind/IO.bindTask 将其解包。
等待任务完成,然后返回其结果。
IO.waitAny {α : Type} (tasks : List (Task α)) (h : tasks.length > 0 := by exact Nat.zero_lt_succ _) : BaseIO αIO.waitAny {α : Type} (tasks : List (Task α)) (h : tasks.length > 0 := by exact Nat.zero_lt_succ _) : BaseIO α
等待列表中的任一任务完成,然后返回其结果。
这些运算符从已有任务创建新任务。
只要可能,最好使用 Task.map 或 Task.bind,而不要在新任务中手动调用 Task.get,因为前两者不会暂时增大线程池。
Task.map.{u_1, u_2} {α : Type u_1} {β : Type u_2} (f : α → β) (x : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : Task βTask.map.{u_1, u_2} {α : Type u_1} {β : Type u_2} (f : α → β) (x : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : Task β
map f x 将函数 f 映射到任务 x 上:它会构造(并立即启动)一个新任务,等待 x 的值可用,然后对结果调用 f。
如果提供了 prio,它就是该任务的优先级。
如果将 sync 设为 true,那么当 x 已经完成时,f 在当前线程上执行;否则在 x 完成所在的线程上执行。此时忽略 prio。仅当执行 f 的开销很小且不会阻塞时才应这样做。
Task.bind.{u_1, u_2} {α : Type u_1} {β : Type u_2} (x : Task α) (f : α → Task β) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : Task βTask.bind.{u_1, u_2} {α : Type u_1} {β : Type u_2} (x : Task α) (f : α → Task β) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : Task β
bind x f 对任务 x 和函数 f 执行单子的“绑定”操作:它会构造(并立即启动)一个新任务,等待 x 的值可用,然后对结果调用 f,得到另一个任务,再运行该任务以取得结果。
如果提供了 prio,它就是该任务的优先级。
如果将 sync 设为 true,那么当 x 已经完成时,f 在当前线程上执行;否则在 x 完成所在的线程上执行。此时忽略 prio。仅当执行 f 的开销很小且不会阻塞时才应这样做。
Task.mapList.{u_1, u_2} {α : Type u_1} {β : Type u_2} (f : List α → β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : Task βTask.mapList.{u_1, u_2} {α : Type u_1} {β : Type u_2} (f : List α → β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : Task β
创建一个任务;当 tasks 中的所有任务都完成后,该任务计算把 f 应用于这些任务结果所得的值。
BaseIO.mapTask.{u_1} {α : Type u_1} {β : Type} (f : α → BaseIO β) (t : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task β)BaseIO.mapTask.{u_1} {α : Type u_1} {β : Type} (f : α → BaseIO β) (t : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task β)
创建一个新任务,等待 t 完成,然后对其结果运行 BaseIO 操作 f。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
EIO.mapTask.{u_1} {α : Type u_1} {ε β : Type} (f : α → EIO ε β) (t : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except ε β))EIO.mapTask.{u_1} {α : Type u_1} {ε β : Type} (f : α → EIO ε β) (t : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except ε β))
创建一个新任务,等待 t 完成,然后对其结果运行 IO 操作 f。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。由于 EIO ε 操作可能抛出 ε 类型的异常,任务结果是 Except ε α。
IO.mapTask.{u_1} {α : Type u_1} {β : Type} (f : α → IO β) (t : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except IO.Error β))IO.mapTask.{u_1} {α : Type u_1} {β : Type} (f : α → IO β) (t : Task α) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except IO.Error β))
创建一个新任务,等待 t 完成,然后对其结果运行 IO 操作 f。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。由于 IO 操作可能抛出 IO.Error 类型的异常,任务结果是 Except IO.Error α。
BaseIO.mapTasks.{u_1} {α : Type u_1} {β : Type} (f : List α → BaseIO β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task β)BaseIO.mapTasks.{u_1} {α : Type u_1} {β : Type} (f : List α → BaseIO β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task β)
创建一个新任务,等待列表 tasks 中的所有任务完成,然后对它们的结果运行 IO 操作 f。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
EIO.mapTasks.{u_1} {α : Type u_1} {ε β : Type} (f : List α → EIO ε β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except ε β))EIO.mapTasks.{u_1} {α : Type u_1} {ε β : Type} (f : List α → EIO ε β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except ε β))
创建一个新任务,等待列表 tasks 中的所有任务完成,然后对它们的结果运行 EIO ε 操作 f。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
IO.mapTasks.{u_1} {α : Type u_1} {β : Type} (f : List α → IO β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except IO.Error β))IO.mapTasks.{u_1} {α : Type u_1} {β : Type} (f : List α → IO β) (tasks : List (Task α)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except IO.Error β))
EIO.mapTasks 的 IO 特化版本。
BaseIO.bindTask.{u_1} {α : Type u_1} {β : Type} (t : Task α) (f : α → BaseIO (Task β)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task β)BaseIO.bindTask.{u_1} {α : Type u_1} {β : Type} (t : Task α) (f : α → BaseIO (Task β)) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task β)
创建一个新任务,等待 t 完成,对其结果运行 IO 操作 f,然后以所得任务继续执行。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动这个新任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
EIO.bindTask.{u_1} {α : Type u_1} {ε β : Type} (t : Task α) (f : α → EIO ε (Task (Except ε β))) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except ε β))EIO.bindTask.{u_1} {α : Type u_1} {ε β : Type} (t : Task α) (f : α → EIO ε (Task (Except ε β))) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except ε β))
创建一个新任务,等待 t 完成,对其结果运行 EIO ε 操作 f,然后以所得任务继续执行。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动这个新任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。由于 EIO ε 操作可能抛出 ε 类型的异常,任务结果是 Except ε α。
IO.bindTask.{u_1} {α : Type u_1} {β : Type} (t : Task α) (f : α → IO (Task (Except IO.Error β))) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except IO.Error β))IO.bindTask.{u_1} {α : Type u_1} {β : Type} (t : Task α) (f : α → IO (Task (Except IO.Error β))) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO (Task (Except IO.Error β))
创建一个新任务,等待 t 完成,对其结果运行 IO 操作 f,然后以所得任务继续执行。新任务的优先级为 prio。
运行所得的 BaseIO 操作会立即启动这个新任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。由于 IO 操作可能抛出 IO.Error 类型的异常,任务结果是 Except IO.Error α。
BaseIO.chainTask.{u_1} {α : Type u_1} (t : Task α) (f : α → BaseIO Unit) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO UnitBaseIO.chainTask.{u_1} {α : Type u_1} (t : Task α) (f : α → BaseIO Unit) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : BaseIO Unit
创建一个新任务,等待 t 完成,然后对其结果运行 IO 操作 f。新任务的优先级为 prio。
这是忽略结果值的 BaseIO.mapTask 版本。
运行所得的 BaseIO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
EIO.chainTask.{u_1} {α : Type u_1} {ε : Type} (t : Task α) (f : α → EIO ε Unit) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : EIO ε UnitEIO.chainTask.{u_1} {α : Type u_1} {ε : Type} (t : Task α) (f : α → EIO ε Unit) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : EIO ε Unit
创建一个新任务,等待 t 完成,然后对其结果运行 EIO ε 操作 f。新任务的优先级为 prio。
这是忽略结果值的 EIO.mapTask 版本。
运行所得的 EIO ε 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止该操作或让它对最后一个引用被丢弃作出其他响应,act 应通过 IO.checkCanceled 显式检查取消。
IO.chainTask.{u_1} {α : Type u_1} (t : Task α) (f : α → IO Unit) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : IO UnitIO.chainTask.{u_1} {α : Type u_1} (t : Task α) (f : α → IO Unit) (prio : Task.Priority := Task.Priority.default) (sync : Bool := false) : IO Unit
创建一个新任务,等待 t 完成,然后对其结果运行 IO 操作 f。新任务的优先级为 prio。
这是忽略结果值的 IO.mapTask 版本。
运行所得的 IO 操作会立即启动任务。与通过 Task.spawn 创建的纯任务不同,即使任务的最后一个引用被丢弃,此函数创建的任务仍会运行。如果此时应终止 act 或让它对最后一个引用被丢弃作出其他响应,应通过 IO.checkCanceled 显式检查取消。
非纯任务应使用 IO.checkCanceled 响应取消;取消可能由 IO.cancel 引发,也可能在任务的最后一个引用被丢弃时发生。
纯任务会在取消时自动终止。
返回 Lean 运行时任务管理器中任务的当前状态。
对于派生自 Promise 的任务,应将 waiting 和 running 状态视为等价。
Lean 运行时任务管理器中 Task 的当前状态。
构造子
IO.TaskState.waiting : IO.TaskState
Task 正在等待运行。
它可能正在等待依赖项完成,也可能位于任务管理器队列中等待可用的运行线程。
IO.TaskState.running : IO.TaskState
Task 正在某个线程上运行;对于 Promise,则表示正在等待调用 IO.Promise.resolve。
承诺表示一个将在未来提供的值。 提供该值称为兑现承诺。 承诺创建后,可以像其他值一样存入数据结构或四处传递;尝试读取它时会阻塞,直至它兑现。
创建一个新的 Promise。
检查承诺是否已经解析,即访问 result* 是否会立即返回。
Promise 的结果任务。
该任务会阻塞,直到调用 Promise.resolve。如果承诺在从未解析的情况下被丢弃,对任务求值将引发恐慌;不使用致命恐慌时,则会永远阻塞。由于 Promise.result! 是纯值,可能无法准确得知其求值时点,因此任何可能对其求值 Promise.result! 的承诺都必须最终得到解析。如有疑问,应始终优先使用 Promise.result? 显式处理被丢弃的承诺。
与 Promise.result 类似,但如果承诺在从未解析的情况下被丢弃,则解析为 dflt。
解析一个 Promise。
只有第一次调用此函数会产生效果。
除本节介绍的类型与操作外,IO.Ref 也可用作锁。
取走引用(使用 take)会使其他线程在读取时阻塞,直到再次用 set 设置该引用。
这种模式在引用单元一节中介绍。
导入 Std.Sync.Channel 后即可使用本节中的类型与函数。
一种多生产者、多消费者的 FIFO 通道,既支持有界和无界缓冲,也提供异步 API。使用 Channel.sync 可切换到同步模式。
如果通道需要通过关闭来表示某种完成事件,请改用 Std.CloseableChannel。请注意,Std.CloseableChannel 在某些情况下需要错误处理,因此适用时通常更容易使用 Std.Channel。
通过通道发送一个值,并返回一个在传输完成后解析的任务。
Std.Channel.forAsync {α : Type} [Inhabited α] (f : α → BaseIO Unit) (ch : Std.Channel α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task Unit)Std.Channel.forAsync {α : Type} [Inhabited α] (f : α → BaseIO Unit) (ch : Std.Channel α) (prio : Task.Priority := Task.Priority.default) : BaseIO (Task Unit)
ch.forAsync f 对从 ch 接收的每条消息调用 f。
请注意,如果调用此函数两次,每条消息只会到达其中恰好一次调用。
此函数不执行任何操作,只是用于方便地公开通道的同步 API。
一种多生产者、多消费者的 FIFO 通道,既支持有界和无界缓冲,也提供同步 API。此类型只是以阻塞方式使用通道的便捷层,与原通道并无实际区别。
如果通道需要通过关闭来表示某种完成事件,请改用 Std.CloseableChannel.Sync。请注意,Std.CloseableChannel.Sync 在某些情况下需要错误处理,因此适用时通常更容易使用 Std.Channel.Sync。
一种多生产者、多消费者的 FIFO 通道,既支持有界和无界缓冲,也提供异步 API;使用 CloseableChannel.sync 可切换到同步模式。
此外,与 Std.Channel 不同,Std.CloseableChannel 可在需要时关闭。这在某些情况下会带来错误处理的需要,因此适用时通常更容易使用 Std.Channel。
Std.CloseableChannel.new {α : Type} (capacity : Option Nat := none) : BaseIO (Std.CloseableChannel α)Std.CloseableChannel.new {α : Type} (capacity : Option Nat := none) : BaseIO (Std.CloseableChannel α)
同步通道也可使用 Lean.Parser.Term.doFor : doElemfor 循环读取。
具体而言,对于每个具有 MonadLiftT BaseIO m 实例的单子 m,以及每个具有 Inhabited α 实例的 α,都存在类型为 ForIn m (Std.Channel.Sync α) α 的实例。
导入 Std.Sync.Mutex 后即可使用本节中的类型与函数。
创建一个新的互斥锁。
Std.Mutex.atomically {m : Type → Type} {α β : Type} [Monad m] [MonadLiftT BaseIO m] [MonadFinally m] (mutex : Std.Mutex α) (k : Std.AtomicT α m β) : m βStd.Mutex.atomically {m : Type → Type} {α β : Type} [Monad m] [MonadLiftT BaseIO m] [MonadFinally m] (mutex : Std.Mutex α) (k : Std.AtomicT α m β) : m β
mutex.atomically k 在锁定互斥锁期间运行 k,使其可访问互斥锁的状态。
如果同一线程已持有底层 BaseMutex,再调用 mutex.atomically 属于未定义行为。如果代码无法避免这种情况,请考虑使用 RecursiveMutex。
Std.Mutex.atomicallyOnce {m : Type → Type} {α β : Type} [Monad m] [MonadLiftT BaseIO m] [MonadFinally m] (mutex : Std.Mutex α) (condvar : Std.Condvar) (pred : Std.AtomicT α m Bool) (k : Std.AtomicT α m β) : m βStd.Mutex.atomicallyOnce {m : Type → Type} {α β : Type} [Monad m] [MonadLiftT BaseIO m] [MonadFinally m] (mutex : Std.Mutex α) (condvar : Std.Condvar) (pred : Std.AtomicT α m Bool) (k : Std.AtomicT α m β) : m β
mutex.atomicallyOnce condvar pred k 运行 k,并在 condvar 上等待,直到 pred 返回 true。k 和 pred 都可以访问互斥锁的状态。
如果同一线程已持有底层 BaseMutex,再调用 mutex.atomicallyOnce 属于未定义行为。如果代码无法避免这种情况,请考虑使用 RecursiveMutex。
导入 Std.Sync.Mutex 后即可使用本节中的类型与函数。
条件变量是一种与 BaseMutex 或 Mutex 配合使用的同步原语。
希望修改共享变量的线程必须:
锁定 BaseMutex 或 Mutex
操作共享变量
完成后调用 Condvar.notifyOne 或 Condvar.notifyAll。请注意,这可以在解锁互斥锁之前或之后进行。
若使用 Mutex,等待 Condvar 的线程可以使用 Mutex.atomicallyOnce 等待条件成立。若使用 BaseMutex,则必须:
锁定 BaseMutex。
执行以下操作之一:
使用 Condvar.waitUntil 在条件变量上(可能反复)等待,直到条件成立。
按以下步骤手动实现等待:
检查条件
调用 Condvar.wait;它会释放 BaseMutex 并暂停执行,直到条件变量收到通知。
检查条件;如果尚未满足,则继续等待。
等待,直到另一线程调用 notifyOne 或 notifyAll。
唤醒一个正在执行 wait 的其他线程。
唤醒所有正在执行 wait 的其他线程。
Std.Condvar.waitUntil.{u_1} {m : Type → Type u_1} [Monad m] [MonadLiftT BaseIO m] (condvar : Std.Condvar) (mutex : Std.BaseMutex) (pred : m Bool) : m UnitStd.Condvar.waitUntil.{u_1} {m : Type → Type u_1} [Monad m] [MonadLiftT BaseIO m] (condvar : Std.Condvar) (mutex : Std.BaseMutex) (pred : m Bool) : m Unit
在条件变量上等待,直到谓词为真。