一、需求来源
flutter 项目中会遇到多文件上传或者下载之类的批量执行任务就需要控制并发数量(避免内存爆炸或者数据传输效率过差),今天灵光一闪,实现一个极简的,分享给大家。
内存爆炸: 一般表现为app闪退。
数据传输效率过差:一般表现为并发数量较多时,每个任务的速度都很慢,减少并发数可以显著提升速度(上传下载同理)。
二、使用示例
假设任务总数是 10,最大并发是 3,每个任务耗时 1 秒完成,执行过程如下:
》执行任务0
》执行任务1
》执行任务2
》任务0 完成,3开始
》任务1 完成,4开始
》任务2 完成,5开始
》任务3 完成,6开始
》任务4 完成,7开始
》任务5 完成,8开始
》任务6 完成,9开始
》任务7 完成
》任务8 完成
》任务9 完成

三、源码
dart
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/extension/dlog.dart';
import 'package:get/get.dart';
class ConcurrentExecutorDemo extends StatefulWidget {
const ConcurrentExecutorDemo({
super.key,
this.arguments,
});
final Map<String, dynamic>? arguments;
@override
State<ConcurrentExecutorDemo> createState() => _ConcurrentExecutorDemoState();
}
class _ConcurrentExecutorDemoState extends State<ConcurrentExecutorDemo> {
final scrollController = ScrollController();
Map<String, dynamic> arguments = Get.arguments ?? <String, dynamic>{};
/// 任务数
var taskCount = 10;
/// 并发数量
var maxConcurrent = 3;
/// 执行描述
final descVN = ValueNotifier("");
String get timeStr => DateTime.now().toString();
@override
void didUpdateWidget(covariant ConcurrentExecutorDemo oldWidget) {
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("$widget"),
),
body: buildBody(),
);
}
Widget buildBody() {
return Scrollbar(
controller: scrollController,
child: SingleChildScrollView(
controller: scrollController,
child: Column(
children: [
Text("并发执行(总数:$taskCount, 最大并发数: $maxConcurrent):"),
ElevatedButton(onPressed: onTest, child: Text("开始执行")),
ValueListenableBuilder(
valueListenable: descVN,
builder: (context, value, child) {
return Text(value);
},
),
],
),
),
);
}
Future<void> onTest() async {
descVN.value = "";
final executor = ConcurrentExecutor(maxConcurrent: maxConcurrent);
for (var i = 0; i < taskCount; i++) {
final id = i;
executor.add(() async {
DLog.d('Start $id');
descVN.value += "[$timeStr]Start $id\n";
await Future.delayed(Duration(seconds: 1));
DLog.d('Done $id');
descVN.value += "[$timeStr]Done $id\n";
});
}
await executor.waitForEmpty();
DLog.d('All tasks complete');
descVN.value += "[$timeStr]All tasks complete";
}
}
/// 并发执行器
class ConcurrentExecutor {
ConcurrentExecutor({this.maxConcurrent = 3});
final int maxConcurrent;
final Queue<Future<void> Function()> _taskQueue = Queue();
int _running = 0;
void add(Future<void> Function() task) {
_taskQueue.add(task);
_tryExecuteNext();
}
void _tryExecuteNext() {
while (_running < maxConcurrent && _taskQueue.isNotEmpty) {
final task = _taskQueue.removeFirst();
_running++;
task().whenComplete(() {
_running--;
_tryExecuteNext();
});
}
}
Future<void> waitForEmpty() async {
while (_taskQueue.isNotEmpty || _running > 0) {
await Future.delayed(Duration(milliseconds: 50));
}
}
}
最后、总结
1、本质上任务管理类实现一个任务队列,根据当前任务执行情况,在限定最大并发数的前提开启新任务,直到清空队列任务的动态过程管理。多用于任务(可能会占用高内存)且多数量(可能导致内存爆炸),和线程池中线程复用是同一个原理。也算是软件开发中具体场景的唯一真解,属于中高阶软件开发展必须掌握的技能。
2、典型场景:批量音视频上传下载,云盘同步等