You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:解决任务队列中任务嵌套入队导致的死锁问题

Flutter:解决任务队列中任务嵌套入队导致的死锁问题

嘿,这个死锁问题我太熟了!你现在的代码里,当doSomething里调用doSomethingElse时,新任务被丢进队列,但队列的处理循环要等doSomething完全跑完才会动,可doSomething又在等队列里的doSomethingElse完成,这不就互相卡着了嘛!

先拆解核心矛盾:你的_begin方法是等当前任务彻底完成后才会去处理队列里的下一个任务,但嵌套入队的任务需要在当前任务的执行过程中就被处理,不然就形成循环等待了。

下面是修复后的完整代码,我会标注关键修改点:

import 'dart:async';

Future<void> main() async {
  final a = doSomething(1);
  final b = doSomethingElse(2);
  final c = doSomething(3);
  await Future.wait([a, b, c]);
  print('done');
}

final queue = TaskQueue();

Future<void> doSomething(int count) async {
  return queue.enqueue(() async {
    await Future.delayed(const Duration(milliseconds: 500));
    print('something #$count');
    await doSomethingElse(count);
  });
}

Future<void> doSomethingElse(int count) {
  return queue.enqueue(() => print('something else #$count'));
}

typedef Task<T> = FutureOr<T> Function();

class TaskQueue {
  final _queue = <_TaskEntry>[];
  var _running = false;
  // 新增:标记当前是否正在处理队列循环,避免嵌套触发重复处理
  var _isProcessingQueue = false;

  bool get isRunning => _running;

  // 抽离队列处理逻辑,单独成方法
  Future _processQueue() async {
    if (_isProcessingQueue) return;
    _isProcessingQueue = true;
    try {
      while (_queue.isNotEmpty) {
        final task = _queue.removeAt(0);
        await task.run();
      }
    } finally {
      _isProcessingQueue = false;
      _running = false;
    }
  }

  Future<T> enqueue<T>(Task<T> task) {
    // 关键判断:如果当前有任务在运行,且不是在当前任务的调用栈内入队,才加入队列
    if (_running && !Zone.current['_inTaskQueue']!) {
      final completer = Completer<T>();
      _queue.add(_TaskEntry(task, completer));
      return completer.future;
    }

    // 如果是在任务内部入队,直接执行,同时标记当前处于任务队列的调用栈中
    return runZoned(() async {
      _running = true;
      try {
        final result = await task();
        // 当前任务执行片段完成后,立即处理队列剩余任务
        await _processQueue();
        return result;
      } catch (e, s) {
        await _processQueue();
        rethrow;
      }
    }, zoneValues: {'_inTaskQueue': true});
  }
}

class _TaskEntry<T> {
  final Task<T> task;
  final Completer<T> completer;

  _TaskEntry(this.task, this.completer);

  Future run() async {
    try {
      final result = await task();
      completer.complete(result);
    } catch (error, stack) {
      completer.completeError(error, stack);
    }
  }
}

关键修改说明

  1. 用Zone标记调用上下文:通过runZoned给每个任务的执行环境打上_inTaskQueue标记,这样就能精准判断当前的enqueue调用是来自外部还是正在运行的任务内部。
  2. 抽离队列处理逻辑:把原来_begin里的队列循环拆成_processQueue方法,确保每次任务片段完成后,都能立即检查并处理队列里的等待任务,不用等整个外层任务结束。
  3. 动态调整执行策略:
    • 外部入队的任务:依然加入队列等待,保证顺序执行;
    • 任务内部嵌套入队的任务:直接同步执行,执行完后立刻处理队列剩余任务,打破循环等待。

现在运行代码,你会得到完全符合预期的输出:

something #1
something else #1
something else #2
something #3
something else #3
done

这样既解决了死锁问题,又完美保留了任务队列的顺序执行特性~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 08:34:34