已发布 上游基线 bf46254 原文 ↗ 在 GitHub 编辑

Latch

Latch 通过让 Fiber 等待某个特定事件发生来同步它们,并根据其打开或关闭的状态控制访问。

Latch 是一种同步工具,其行为如同一道闸门:它让 Fiber 先等待,直到 Latch 被打开之后才继续执行。Latch 可以处于打开或关闭两种状态:

  • 关闭时,到达 Latch 的 Fiber 会一直等待,直到它被打开。
  • 打开时,Fiber 会立即通过。

一旦被打开,Latch 通常会保持打开状态,不过如有需要,你也可以再次将它关闭。

设想有一个应用,它只有在完成初始化设置(例如加载配置数据或建立数据库连接)之后才处理请求。 你可以在设置进行期间创建一个处于关闭状态的 Latch。 任何到达的请求(以 Fiber 表示)都会在 Latch 处等待,直到它被打开。 一旦设置完成,你调用 latch.open,请求便得以继续。

Latch 接口

Latch 包含若干操作,让你能够控制并观察它的状态:

操作说明
whenOpen仅当 Latch 处于打开状态时才运行给定的 effect;否则,等待直到它被打开。
open打开 Latch,让所有正在等待的 Fiber 得以继续。
close关闭 Latch,使 Fiber 在之后到达该 Latch 时进行等待。
await挂起当前 Fiber,直到 Latch 被打开。如果 Latch 已经处于打开状态,则立即返回。
release让正在等待的 Fiber 继续执行,但不会永久打开 Latch。

创建 Latch

使用 Effect.makeLatch 函数并传入一个布尔值,即可创建一个处于打开或关闭状态的 Latch。默认值为 false,也就是说它初始处于关闭状态。

示例(创建并使用 Latch)

在这个示例中,Latch 初始处于关闭状态。一个 Fiber 仅在 Latch 打开时才输出 “open sesame” 日志。等待一秒之后,Latch 被打开,该 Fiber 随之被释放:

import { Console, Effect } from "effect"

// A generator function that demonstrates latch usage
const program = Effect.gen(function* () {
  // Create a latch, starting in the closed state
  const latch = yield* Effect.makeLatch()

  // Fork a fiber that logs "open sesame" only when the latch is open
  const fiber = yield* Console.log("open sesame").pipe(
    latch.whenOpen, // Waits for the latch to open
    Effect.fork, // Fork the effect into a new fiber
  )

  // Wait for 1 second
  yield* Effect.sleep("1 second")

  // Open the latch, releasing the fiber
  yield* latch.open

  // Wait for the forked fiber to finish
  yield* fiber.await
})

Effect.runFork(program)
// Output: open sesame (after 1 second)

Latch 与 Semaphore 的对比

当你有一个一次性的事件或条件来决定 Fiber 能否继续执行时,Latch 是合适的选择。例如,你可以用 Latch 阻塞所有 Fiber,直到某个设置步骤完成,然后再打开 Latch,让所有 Fiber 继续执行。

而带一个锁的 semaphore(通常称为 binary semaphore 或 mutex)通常用于互斥:它确保同一时刻只有一个 Fiber 访问共享资源或代码段。一旦某个 Fiber 获取了锁,在锁被释放之前,其他 Fiber 都无法进入受保护的区域。

简而言之:

  • 如果你要用某个特定事件来闸控一组 Fiber(“在这里等待,直到条件成立”),请使用 Latch
  • 如果你需要确保同一时刻只有一个 Fiber 处于临界区或使用共享资源,请使用 Semaphore(仅带一个锁)