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

相关推荐
小周同学:5 分钟前
在 Vue2 中使用 pdf.js + pdf-lib 实现 PDF 预览、手写签名、文字批注与高保真导出
开发语言·前端·javascript·vue.js·pdf
m0_4947166823 分钟前
CSS中实现一个三角形
前端·css
teeeeeeemo1 小时前
跨域及解决方案
开发语言·前端·javascript·笔记
JSON_L1 小时前
Vue Vant应用-数据懒加载
前端·javascript·vue.js
可爱小仙子1 小时前
vue-quill-editor上传图片vue3
前端·javascript·vue.js
じòぴé南冸じょうげん1 小时前
解决ECharts图表上显示的最小刻度不是设置的min值的问题
前端·javascript·echarts
小高0071 小时前
第一章 桃园灯火初燃,响应义旗始揭
前端·javascript·vue.js
小高0071 小时前
第二章 虎牢关前初试Composition,吕布持v-model搦战
前端·javascript·vue.js
清和已久2 小时前
nginx高性能web服务器
服务器·前端·nginx