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); } } }
关键修改说明
- 用Zone标记调用上下文:通过
runZoned给每个任务的执行环境打上_inTaskQueue标记,这样就能精准判断当前的enqueue调用是来自外部还是正在运行的任务内部。 - 抽离队列处理逻辑:把原来
_begin里的队列循环拆成_processQueue方法,确保每次任务片段完成后,都能立即检查并处理队列里的等待任务,不用等整个外层任务结束。 - 动态调整执行策略:
- 外部入队的任务:依然加入队列等待,保证顺序执行;
- 任务内部嵌套入队的任务:直接同步执行,执行完后立刻处理队列剩余任务,打破循环等待。
现在运行代码,你会得到完全符合预期的输出:
something #1 something else #1 something else #2 something #3 something else #3 done
这样既解决了死锁问题,又完美保留了任务队列的顺序执行特性~
内容来源于stack exchange
相关产品推荐
相关产品推荐

