Flutter 如何在dart中实现具有多个并发工作线程(异步)的异步任务队列

Flutter 如何在dart中实现具有多个并发工作线程(异步)的异步任务队列,flutter,asynchronous,dart,queue,Flutter,Asynchronous,Dart,Queue,我的目标是在dart中创建一种网络爬虫。为此,我希望维护一个任务队列,其中存储需要爬网的元素(例如URL)。元素在爬网函数中被爬网,该函数返回需要处理的更多元素的列表。因此,这些元素被添加到队列中。示例代码: 导入“省道:集合”; 最终队列=队列(); main()异步{ 队列 …添加(“…”) …添加(“…”) …添加(“…”); while(queue.isNotEmpty){ 结果=等待爬网(queue.removeFirst()); queue.addAll(结果); } } 未来爬网(

我的目标是在dart中创建一种网络爬虫。为此,我希望维护一个任务队列,其中存储需要爬网的元素(例如URL)。元素在爬网函数中被爬网,该函数返回需要处理的更多元素的列表。因此,这些元素被添加到队列中。示例代码:

导入“省道:集合”;
最终队列=队列();
main()异步{
队列
…添加(“…”)
…添加(“…”)
…添加(“…”);
while(queue.isNotEmpty){
结果=等待爬网(queue.removeFirst());
queue.addAll(结果);
}
}
未来爬网(字符串x)异步{
...
res=等待http.get(x)
...
返回结果;
}
此粗略代码一次仅处理一个元素。但是,我希望有一个工作人员池(例如5),将元素从队列中取出,同时处理它们,并将结果添加回队列。由于瓶颈是HTTP请求,我认为使用多个worker调用Future.wait()可以加快执行速度。然而,我不想让服务器过载,因此我也想限制工作人员的数量


这可以用基本的异步原语和信号量来实现吗?我希望尽可能避免隔离,以使解决方案尽可能简单。

我不知道是否已经有一个软件包提供此功能,但由于编写自己的逻辑并不复杂,因此我制作了以下示例:

import 'dart:async';
import 'dart:collection';
import 'dart:math';

class TaskRunner<A, B> {
  final Queue<A> _input = Queue();
  final StreamController<B> _streamController = StreamController();
  final Future<B> Function(A) task;

  final int maxConcurrentTasks;
  int runningTasks = 0;

  TaskRunner(this.task, {this.maxConcurrentTasks = 5});

  Stream<B> get stream => _streamController.stream;

  void add(A value) {
    _input.add(value);
    _startExecution();
  }

  void addAll(Iterable<A> iterable) {
    _input.addAll(iterable);
    _startExecution();
  }

  void _startExecution() {
    if (runningTasks == maxConcurrentTasks || _input.isEmpty) {
      return;
    }

    while (_input.isNotEmpty && runningTasks < maxConcurrentTasks) {
      runningTasks++;
      print('Concurrent workers: $runningTasks');

      task(_input.removeFirst()).then((value) async {
        _streamController.add(value);

        while (_input.isNotEmpty) {
          _streamController.add(await task(_input.removeFirst()));
        }

        runningTasks--;
        print('Concurrent workers: $runningTasks');
      });
    }
  }
}

Random _rnd = Random();
Future<List<String>> crawl(String x) =>
    Future.delayed(Duration(seconds: _rnd.nextInt(5)), () => x.split('-'));

void main() {
  final runner = TaskRunner(crawl, maxConcurrentTasks: 3);

  runner.stream.forEach((listOfString) {
    if (listOfString.length == 1) {
      print('DONE: ${listOfString.first}');
    } else {
      print('PUTTING STRINGS ON QUEUE: $listOfString');
      runner.addAll(listOfString);
    }
  });

  runner.addAll(['1-2-3-4-5-6-7-8-9', '10-20-30-40-50-60-70-80-90']);
}
我相信这个类的可用性可以提高,但我认为核心概念很容易理解。概念是我们定义了一个
队列
,每次我们向这个
队列
添加东西时,我们都会检查是否可以开始执行新的异步任务。否则我们就跳过它,因为我们确保每个当前运行的异步任务都会在“关闭”之前检查
队列中的更多内容

结果由
流返回,您可以订阅该流,例如,根据我在示例中显示的结果向
TaskRunner
添加更多内容。返回数据的顺序基于数据完成的顺序


重要的是,这不是在多个线程中运行任务的方法。所有代码都在一个Dart隔离线程中运行,但由于HTTP请求被IO延迟,因此有必要尝试生成多个
Future
,然后等待结果。

太棒了,非常感谢!在您的示例中,为什么即使maxConcurrentTasks=3,也会显示“并发工作者:4”?行
while(\u input.isNotEmpty&&runningTasks)