Dart锁机制之synchronized源码解析Lock、BasicLock、objectMakeLock、objectSynchronized(一)

78 阅读11分钟

synchronized

synchronized是一个优秀的锁定机制,用于防止并发访问异步代码。

目标是提出一种类似于临界区(criminal sections)的解决方案,并提供一个类似 Java 风格的简单 synchronized API。它提供了一个基本的锁/互斥锁解决方案,以支持事务等功能。

synchronized的工作原理就是下一个任务会在前一个任务完成后执行

没有加锁的异步任务

Future<void> writeSlow(int value) async {
  await Future<void>.delayed(const Duration(milliseconds: 1));
  stdout.write(value);
}

Future<void> write(List<int> values) async {
  for (var value in values) {
    await writeSlow(value);
  }
}

上面两个函数,write函数会循环遍历调用writeSlow函数并同步等待writeSlow任务完成writeSlow在控制台简单打印日志。

Future<void> write1234() async {
  await write([1, 2, 3, 4]);
}

write1234函数传入具体任务,执行后控制台会打印日志1234

Future<void> test1() async {
  stdout.writeln('not synchronized');
  //await Future.wait([write1234(), write1234()]);
  // ignore: unawaited_futures
  write1234();
  // ignore: unawaited_futures
  write1234();

  await Future<void>.delayed(const Duration(milliseconds: 50));
  stdout.writeln();
}

test1函数内部会执行两次write1234函数。控制台会打印:

11223344

截屏2025-12-01 11.34.11.png

从打印日志看, 第二次的任务并没有等待第一次的任务全部执行完成后再执行,而是两次任务交替执行了。

加锁的异步任务

同样的多个任务, 加锁后就可以保证各个任务同步执行了。

Future<void> runSynchronized() async {
  stdout.writeln('synchronized');

  final lock = Lock();
  // this should print 12341234
  // ignore: unawaited_futures
  lock.synchronized(write1234);
  // ignore: unawaited_futures
  lock.synchronized(write1234);

  await Future<void>.delayed(const Duration(milliseconds: 50));

  stdout.writeln();
}

runSynchronized函数内部用同步锁Lock控制两个任务write1234。执行后, 控制台会打印:

12341234

截屏2025-12-01 11.40.24.png

这样第二次任务就会等待第一个任务执行完成后再执行。

Lock

/// Create a shared [Lock] object and use it using the [Lock.synchronized] /// 创建一个共享的 [Lock] 对象,并通过 [Lock.synchronized] /// method to prevent concurrent access to a shared resource. /// 方法来防止并发访问共享资源。

/// Object providing the implicit lock.
/// 提供隐式锁的对象。
///
/// A [Lock] can be reentrant (in this case it will use a [Zone]).
/// [Lock] 可以是可重入的(此时会使用 [Zone])。
///
/// non-reentrant lock is used like an aync executor with a capacity of 1.
/// 非重入锁的使用类似容量为 1 的异步执行器。
///
/// if [timeout] is not null, it will timeout after the specified duration.
/// 如果 [timeout] 不为 null,则会在指定持续时间后超时。
abstract class Lock {
  /// Creates a [Lock] object.
  /// 创建一个 [Lock] 对象。
  ///
  /// if [reentrant], it uses [Zone] to allow inner [synchronized] calls.
  /// 如果启用 [reentrant],则使用 [Zone] 以允许嵌套的 [synchronized] 调用。
  factory Lock({bool reentrant = false}) {
    if (reentrant) {
      return ReentrantLock();
    } else {
      return BasicLock();
    }
  }

  /// Executes [computation] when lock is available.
  /// 当锁可用时执行 [computation]。
  ///
  /// Only one asynchronous block can run while the lock is retained.
  /// 在持有锁期间,只有一个异步代码块可以运行。
  ///
  /// If [timeout] is specified, it will try to grab the lock and will not
  /// 如果指定了 [timeout],它会尝试获取锁,并且在无法在给定时长内获取锁时,
  /// call the computation callback and throw a [TimeoutExpection] if the lock
  /// 不会调用计算回调,并会抛出 [TimeoutExpection]。
  /// cannot be grabbed in the given duration.
  /// 如果在给定时长内无法获取锁。
  Future<T> synchronized<T>(
    FutureOr<T> Function() computation, {
    Duration? timeout,
  });

  /// returns true if the lock is currently locked.
  /// 如果当前锁已被持有则返回 true。
  bool get locked;

  /// for reentrant, test whether we are currently in the synchronized section.
  /// 对于可重入锁,测试当前是否处于 synchronized 区段内。
  /// for non reentrant, it returns the [locked] status.
  /// 对于非重入锁,它返回 [locked] 状态。
  ///
  /// For non-reentrant lock, it matches the [locked] status and since it does
  /// 对非重入锁,此值与 [locked] 状态一致;由于语义并不明确,
  /// not mean anything, it should not be used as behavior may change in the future.
  /// 不应依赖该值,未来行为可能会发生变化。
  bool get inLock;

  /// It returns true if the lock can be locked. For basic lock (reentrant or
  /// 当锁可以被加锁时返回 true。对于基础锁(可重入或不可重入),
  /// not), it is when the lock is not locked.
  /// 当锁未被持有时为 true。
  ///
  /// For multi lock, it is when any lock is locked.
  /// 对于多锁(MultiLock),当任一锁被持有时,相应行为按实现定义。
  bool get canLock;
}

BasicLock

BasicLock真正实现加锁机制, 加锁原理。

/// Basic (non-reentrant) lock
class BasicLock implements Lock {
  /// The last running block
  Future<dynamic>? last;

  @override
  bool get locked => last != null;

  @override
  bool get canLock => !locked;

  @override
  Future<T> synchronized<T>(FutureOr<T> Function() func, {
    Duration? timeout,
  }) async {
    print('\n ');
    var step = 0;
    String addr(Object? o) => o == null ? 'null' : '${o.runtimeType}@${identityHashCode(o)}';
    void log(String m) {
      print('✅ $m(步骤=${++step})');
    }
    log('进入 synchronized:locked=$locked last=${addr(last)} timeout=$timeout func=$func');
    log('赋值 prev=last 之前:last=${addr(last)}');
    final prev = last;
    log('赋值 prev=last 之后:prev=${addr(prev)}');
    log('创建 completer 之前');
    final completer = Completer<void>.sync();
    log('创建 completer 之后:completer=${addr(completer)} completer.future=${addr(completer.future)}');
    log('设置 last=completer.future 之前:last=${addr(last)}');
    last = completer.future;
    log('设置 last 之后:last=${addr(last)}');
    try {
      log('准备等待前一个任务(如需要):prev=${addr(prev)} timeout=$timeout');
      // If there is a previous running block, wait for it
      if (prev != null) {
        log('prev 非空,开始等待');
        if (timeout != null) {
          log('调用 prev.timeout 前:prev=${addr(prev)} timeout=$timeout');
          // This could throw a timeout error
          await prev.timeout(timeout);
          log('调用 prev.timeout 后');
        } else {
          log('开始 await prev');
          await prev;
          log('await prev 完成');
        }
      } else {
        log('prev 为空,无需等待');
      }
      log('等待步骤完成');

      log('调用 func() 之前');
      // Run the function and return the result
      var result = func();
      log('调用 func() 之后:result=$result type=${result.runtimeType}');
      if (result is Future) {
        log('result 是 Future,开始 await');
        final awaited = await result;
        log('await result 完成:value=$awaited type=${awaited.runtimeType}');
        log('返回异步结果');
        print('\n ');
        return awaited;
      } else {
        log('返回同步结果:value=$result type=${result.runtimeType}');
        print('\n ');
        return result;
      }
    } finally {
      log('进入 finally 清理:last=${addr(last)} prev=${addr(prev)} timeout=$timeout');
      // Cleanup
      // waiting for the previous task to be done in case of timeout
      void complete() {
        log('complete() 开始:last=${addr(last)} completer=${addr(completer)} completer.future=${addr(completer.future)}');
        // Only mark it unlocked when the last one complete
        if (identical(last, completer.future)) {
          log('last 与 completer.future 相同,设置 last=null');
          last = null;
          log('last 已变为 null');
        } else {
          log('last 与 completer.future 不同,保持 last=${addr(last)}');
        }
        log('调用 completer.complete() 之前');
        completer.complete();
        log('调用 completer.complete() 之后');
      }

      // In case of timeout, wait for the previous one to complete too
      // before marking this task as complete

      if (prev != null && timeout != null) {
        log('prev 与 timeout 均非空,安排 prev.then 回调');
        // But we still returns immediately
        // ignore: unawaited_futures
        prev.then((_) {
          log('prev.then 回调开始');
          complete();
          log('prev.then 回调结束');
        });
        log('已安排 prev.then');
      } else {
        log('直接调用 complete()');
        complete();
        log('complete() 已返回');
      }
      log('退出 finally 清理');
    }
  }

  @override
  String toString() {
    return 'Lock[${identityHashCode(this)}]';
  }

  @override
  bool get inLock => locked;
}
    

特别重要原理:

BasicLock 的实现其实是基于 “Future 串链(chaining)” 模型:

t1 → t2 → t3 → ...
  • 每一个 synchronized 创建一个新的 Future(completer.future)
  • 把它接在 last 的后面
  • 并等待上一个任务完成

整体流程:

  1. A 调用 synchronized

    • last=A
    • A 执行
  2. B 调用 synchronized

    • last=B
    • B 发现 prev=A → 等待 A 完成
  3. A 结束 → complete()

    • last=null(如果当前任务就是 last)
  4. B 开始执行

典型的“互斥锁(mutex)串行执行”效果。

last

 /// The last running block
  Future<dynamic>? last;

记录最后一个执行中的 Future。

last 是一个 Future,代表“当前或最后执行的任务”。
只要 last != null,说明锁被占用,新的任务必须等待它完成。

locked / canLock

@override
bool get locked => last != null;

@override
bool get canLock => !locked;
  • locked: 是否被锁住

  • canLock: 是否可以获取锁

synchronized 方法(核心)

final prev = last;
final completer = Completer<void>.sync();
last = completer.future;
  • prev:保存上一个执行任务

  • completer:创建一个新的任务句柄

  • last 更新为这个新任务的 future

等待上一个任务完成

if (prev != null) {
  if (timeout != null) {
    await prev.timeout(timeout);
  } else {
    await prev;
  }
}

若有上一个任务,则等待它完成;若设置了 timeout 则使用超时等待。

锁本质是:

只要 previous 没完成(complete),当前任务就不能执行。

执行 func()

var result = func();
if (result is Future) {
  return await result;
} else {
  return result;
}

执行用户提供的函数,若其返回 Future 则等待,否则直接返回。

finally(释放锁)

void complete() {
  if (identical(last, completer.future)) {
    last = null;
  }
  completer.complete();
}
  • 若当前任务仍然是 last,则将 last 设为 null(锁释放)

  • 完成当前任务 future(通知下一个等待者)

这里保证:

  1. 锁只会在当前任务完成时释放
  2. 后续任务看到 prev 完成后即可执行

特殊处理:超时时

if (prev != null && timeout != null) {
  prev.then((_) {
    complete();
  });
} else {
  complete();
}

如果你设置了 timeout:

  • 当前任务会 立即返回 timeout 错误
  • 但是锁不能提前释放
  • 所以必须等待真正的 prev 完成后才释放锁

🔥 总体工作原理(非常重要)

BasicLock 的实现其实是基于 “Future 串链(chaining)” 模型:

t1 → t2 → t3 → ...
  • 每一个 synchronized 创建一个新的 Future(completer.future)
  • 把它接在 last 的后面
  • 并等待上一个任务完成

runSynchronized同步执行函数

Future<void> runSynchronized() async {
  stdout.writeln('synchronized');

  final lock = Lock();
  // this should print 12341234
  // ignore: unawaited_futures
  lock.synchronized(write1234);
  // ignore: unawaited_futures
  lock.synchronized(write5678);
  lock.synchronized(write9101112);
  await Future<void>.delayed(const Duration(milliseconds: 50));

  stdout.writeln();
}
Future<void> write1234() async {
  await write([1, 2, 3, 4]);
}

Future<void> write5678() async {
  await write([5, 6, 7, 8]);
}

Future<void> write9101112() async {
  await write([9, 10, 11, 12]);
}

日志如下:

synchronized

 
✅ 进入 synchronized:locked=false last=null timeout=null func=Closure: () => Future<void> from Function 'write1234': static.(步骤=1)
✅ 赋值 prev=last 之前:last=null(步骤=2)
✅ 赋值 prev=last 之后:prev=null(步骤=3)
✅ 创建 completer 之前(步骤=4)
✅ 创建 completer 之后:completer=_SyncCompleter<void>@971821951 completer.future=Future<void>@242937982(步骤=5)
✅ 设置 last=completer.future 之前:last=null(步骤=6)
✅ 设置 last 之后:last=Future<void>@242937982(步骤=7)
✅ 准备等待前一个任务(如需要):prev=null timeout=null(步骤=8)
✅ prev 为空,无需等待(步骤=9)
✅ 等待步骤完成(步骤=10)
✅ 调用 func() 之前(步骤=11)
✅ 调用 func() 之后:result=Instance of 'Future<void>' type=Future<void>(步骤=12)
✅ result 是 Future,开始 await(步骤=13)

 
✅ 进入 synchronized:locked=true last=Future<void>@242937982 timeout=null func=Closure: () => Future<void> from Function 'write5678': static.(步骤=1)
✅ 赋值 prev=last 之前:last=Future<void>@242937982(步骤=2)
✅ 赋值 prev=last 之后:prev=Future<void>@242937982(步骤=3)
✅ 创建 completer 之前(步骤=4)
✅ 创建 completer 之后:completer=_SyncCompleter<void>@919542402 completer.future=Future<void>@352620904(步骤=5)
✅ 设置 last=completer.future 之前:last=Future<void>@242937982(步骤=6)
✅ 设置 last 之后:last=Future<void>@352620904(步骤=7)
✅ 准备等待前一个任务(如需要):prev=Future<void>@242937982 timeout=null(步骤=8)
✅ prev 非空,开始等待(步骤=9)
✅ 开始 await prev(步骤=10)

 
✅ 进入 synchronized:locked=true last=Future<void>@352620904 timeout=null func=Closure: () => Future<void> from Function 'write9101112': static.(步骤=1)
✅ 赋值 prev=last 之前:last=Future<void>@352620904(步骤=2)
✅ 赋值 prev=last 之后:prev=Future<void>@352620904(步骤=3)
✅ 创建 completer 之前(步骤=4)
✅ 创建 completer 之后:completer=_SyncCompleter<void>@1072996005 completer.future=Future<void>@19405319(步骤=5)
✅ 设置 last=completer.future 之前:last=Future<void>@352620904(步骤=6)
✅ 设置 last 之后:last=Future<void>@19405319(步骤=7)
✅ 准备等待前一个任务(如需要):prev=Future<void>@352620904 timeout=null(步骤=8)
✅ prev 非空,开始等待(步骤=9)
✅ 开始 await prev(步骤=10)
1234✅ await result 完成:value=null type=Null(步骤=14)
✅ 返回异步结果(步骤=15)

 
✅ 进入 finally 清理:last=Future<void>@19405319 prev=null timeout=null(步骤=16)
✅ 直接调用 complete()(步骤=17)
✅ complete() 开始:last=Future<void>@19405319 completer=_SyncCompleter<void>@971821951 completer.future=Future<void>@242937982(步骤=18)
✅ last 与 completer.future 不同,保持 last=Future<void>@19405319(步骤=19)
✅ 调用 completer.complete() 之前(步骤=20)
✅ await prev 完成(步骤=11)
✅ 等待步骤完成(步骤=12)
✅ 调用 func() 之前(步骤=13)
✅ 调用 func() 之后:result=Instance of 'Future<void>' type=Future<void>(步骤=14)
✅ result 是 Future,开始 await(步骤=15)
✅ 调用 completer.complete() 之后(步骤=21)
✅ complete() 已返回(步骤=22)
✅ 退出 finally 清理(步骤=23)
5678✅ await result 完成:value=null type=Null(步骤=16)
✅ 返回异步结果(步骤=17)

 
✅ 进入 finally 清理:last=Future<void>@19405319 prev=Future<void>@242937982 timeout=null(步骤=18)
✅ 直接调用 complete()(步骤=19)
✅ complete() 开始:last=Future<void>@19405319 completer=_SyncCompleter<void>@919542402 completer.future=Future<void>@352620904(步骤=20)
✅ last 与 completer.future 不同,保持 last=Future<void>@19405319(步骤=21)
✅ 调用 completer.complete() 之前(步骤=22)
✅ await prev 完成(步骤=11)
✅ 等待步骤完成(步骤=12)
✅ 调用 func() 之前(步骤=13)
✅ 调用 func() 之后:result=Instance of 'Future<void>' type=Future<void>(步骤=14)
✅ result 是 Future,开始 await(步骤=15)
✅ 调用 completer.complete() 之后(步骤=23)
✅ complete() 已返回(步骤=24)
✅ 退出 finally 清理(步骤=25)
9101112✅ await result 完成:value=null type=Null(步骤=16)
✅ 返回异步结果(步骤=17)

 
✅ 进入 finally 清理:last=Future<void>@19405319 prev=Future<void>@352620904 timeout=null(步骤=18)
✅ 直接调用 complete()(步骤=19)
✅ complete() 开始:last=Future<void>@19405319 completer=_SyncCompleter<void>@1072996005 completer.future=Future<void>@19405319(步骤=20)
✅ last 与 completer.future 相同,设置 last=null(步骤=21)
✅ last 已变为 null(步骤=22)
✅ 调用 completer.complete() 之前(步骤=23)
✅ 调用 completer.complete() 之后(步骤=24)
✅ complete() 已返回(步骤=25)
✅ 退出 finally 清理(步骤=26)