Flutter进阶:高内存任务的动态并发执行完美实现

一、需求来源

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、典型场景:批量音视频上传下载,云盘同步等

github

相关推荐
kyriewen6 小时前
微软用Go重写TypeScript编译器,速度提升10倍,网友:这是“背叛”还是“救赎”?
前端·typescript·ecmascript 6
Ceelog6 小时前
久坐党自救指南:屏幕前 8 小时,身体到底在经历什么
前端·后端
西陵6 小时前
Agent 为什么会陷入 Doom Loop?OpenClaw 的破解之道
前端·人工智能·ai编程
Hyyy7 小时前
普通前端续命周报——第2周
前端
wuxinyan1237 小时前
工业级大模型学习之路030:Streamlit 企业级智能体前端工作台
前端·学习·streamlit·智能体
修己xj7 小时前
告别无效刷屏!TrendRadar:最快30秒部署的开源热点助手,让你只看真正关心的新闻
前端
anOnion8 小时前
构建无障碍组件之Slider Pattern
前端·html·交互设计
云水一下8 小时前
JavaScript 从零基础到精通系列:前世今生与编程启蒙
前端·javascript
月亮邮递员6168 小时前
Markdown语法总结
开发语言·前端·javascript