├── .github └── workflows │ └── publish.yml ├── .gitignore ├── .metadata ├── CHANGELOG.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── example └── isolate_pool_executor_example.dart ├── lib ├── isolate_pool_executor.dart └── src │ ├── ext │ └── isolate_pool_executor_ext.dart │ ├── future │ └── task_future.dart │ ├── isolate_pool_executor.dart │ ├── isolate_pool_executor_cache.dart │ ├── isolate_pool_executor_core.dart │ ├── isolate_pool_executor_single.dart │ ├── isolate_pool_task.dart │ └── queue │ ├── queue_empty.dart │ ├── queue_unique.dart │ └── queue_weighted.dart └── pubspec.yaml /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | # .github/workflows/publish.yml 2 | name: Publish to pub.dev 3 | 4 | on: 5 | push: 6 | tags: 7 | # must align with the tag-pattern configured on pub.dev, often just replace 8 | # {{version}} with [0-9]+.[0-9]+.[0-9]+ 9 | - 'v[0-9]+.[0-9]+.[0-9]+' # tag-pattern on pub.dev: 'v{{version}}' 10 | # If you prefer tags like '1.2.3', without the 'v' prefix, then use: 11 | # - '[0-9]+.[0-9]+.[0-9]+' # tag-pattern on pub.dev: '{{version}}' 12 | # If your repository contains multiple packages consider a pattern like: 13 | # - 'my_package_name-v[0-9]+.[0-9]+.[0-9]+' 14 | 15 | # Publish using the reusable workflow from dart-lang. 16 | jobs: 17 | publish: 18 | permissions: 19 | id-token: write # Required for authentication using OIDC 20 | uses: dart-lang/setup-dart/.github/workflows/publish.yml@v1 21 | # with: 22 | # working-directory: path/to/package/within/repository -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | migrate_working_dir/ 12 | 13 | # IntelliJ related 14 | *.iml 15 | *.ipr 16 | *.iws 17 | .idea/ 18 | 19 | # The .vscode folder contains launch configuration and tasks you configure in 20 | # VS Code which you may wish to be included in version control, so this line 21 | # is commented out by default. 22 | #.vscode/ 23 | 24 | # Flutter/Dart/Pub related 25 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 26 | /pubspec.lock 27 | **/doc/api/ 28 | .dart_tool/ 29 | .packages 30 | build/ 31 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 796c8ef79279f9c774545b3771238c3098dbefab 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.1.0 2 | 3 | * customizeTaskInvoker adds new parameters: taskLabel, what, and tag, derived from the corresponding 4 | parameters in compute. 5 | 6 | ### Breaking Changes 7 | 8 | * The tag parameter in compute will be sent to the isolate. Note that you should verify whether the 9 | parameter value can be sent. https://api.dart.dev/dart-isolate/SendPort/send.html 10 | 11 | ## 2.0.0 12 | 13 | * Allow customization of task execution in Isolate. 14 | * Fix `onIsolateCreated` bug 15 | * Improve the ability to capture task exceptions 16 | * Modify debugLabel name 17 | 18 | ## 1.2.3 19 | 20 | * Allow obtaining the content of the message before submitting the task for calculating the order. 21 | 22 | ## 1.2.2 23 | 24 | * Fixed the task execution exception when `taskQueueInIsolate` is set to `true` in `SingleIsolate`. 25 | 26 | ## 1.2.1 27 | 28 | * In non-release mode (determined by `assert`), the default creation waiting time is changed to 6 29 | seconds. In release mode, it remains unchanged at 3 seconds. 30 | 31 | ## 1.2.0 32 | 33 | * Added a new parameter `onIsolateCreateTimeoutTimesDoNotCreateNew`. If isolate creation times 34 | out `n` consecutive times, no new isolates will be created, and only the already initialized 35 | isolates will be used. If no timeouts occur, the pool will use the isolates with `m` cores, along 36 | with other cached isolates. 37 | * This feature can help alleviate [this issue](https://github.com/flutter/flutter/issues/132731). 38 | See the [README](https://github.com/aymtools/isolate_pool_executor/blob/master/README.md) for more 39 | details. 40 | 41 | ## 1.1.5 42 | 43 | * Added the `immediatelyStartedCore` parameter to `IsolatePoolExecutor` 44 | and `IsolatePoolExecutor.newFixedIsolatePool`, allowing customization of the number of isolates 45 | that start immediately. 46 | 47 | ## 1.1.4 48 | 49 | * The `isolateValues` in the `onIsolateCreated` callback is now non-nullable and will default to an 50 | empty map. 51 | * Added `isShutdown` to the pool to check if `shutdown` has been called. 52 | * Updated the README to include instructions on calling `MethodChannel` from an isolate in Flutter. 53 | 54 | ## 1.1.3 55 | 56 | * The return value of `compute` is now wrapped in `TaskFuture`, allowing you to view the 57 | current `taskId` and related `tag`. 58 | 59 | ## 1.1.2 60 | 61 | * Added support for specifying a `debugLabel` for `IsolatePoolExecutor`. 62 | 63 | ## 1.1.1 64 | 65 | * Added a new `onIsolateCreated` parameter, which is called immediately after an isolate is created. 66 | This, along with `isolateValues`, allows initialization of data for the isolate. 67 | * 68 | 69 | Example: [Introducing background isolate channels](https://medium.com/flutter/introducing-background-isolate-channels-7a299609cad8). 70 | 71 | * Interaction with isolates now uses `RawReceivePort`. 72 | 73 | ## 1.1.0 74 | 75 | * Optimized initial isolate startup by directly assigning tasks, reducing one send operation, and 76 | improved idle state detection for isolates. 77 | * Added timeout validation for isolate startup due 78 | to [this known issue](https://github.com/flutter/flutter/issues/132731). 79 | * Added a parameter `launchCoreImmediately` (default `false`), which starts all core isolates 80 | immediately. 81 | 82 | ## 1.0.6 83 | 84 | * Fixed `QueueEmpty` in `newCachedIsolatePool` to prevent adding any tasks. 85 | 86 | ## 1.0.5 87 | 88 | * Threw an exception when sending tasks failed. 89 | * Added global exception handling for workers in isolates. 90 | 91 | ## 1.0.4 92 | 93 | * Added common extension methods. 94 | 95 | ## 1.0.3 96 | 97 | * Optimized communication times in `IsolateNoCache`, bringing performance close to `Isolate.run`. 98 | 99 | ## 1.0.2 100 | 101 | * Added support for storing `map` values during isolate initialization. 102 | * Optimized isolate exit mechanism when `keepAliveTime` is set to 0. 103 | * Improved data transfer between isolates. 104 | 105 | ## 1.0.1 106 | 107 | * Refined code structure. When using a single isolate, tasks can be sent directly to the isolate 108 | without a queue. 109 | 110 | ## 1.0.0 111 | 112 | * First version completed with 3 common creation methods implemented by default. 113 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 aymtools anyaming0@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the “Software”), to deal in the 7 | Software without restriction, including without limitation the rights to use, copy, 8 | modify, merge, publish, distribute, sublicense, and/or sell copies of the 9 | Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The current package provides background creation of isolates to perform CPU-intensive tasks without 2 | affecting the current isolate. 3 | Just like a thread pool, but with isolates. 4 | 5 | ## Usage 6 | 7 | ```dart 8 | int _doTask(int count) { 9 | int sum = 0; 10 | final random = new Random(); 11 | for (int i = 0; i < count; i++) { 12 | sum += random.nextInt(10000); 13 | } 14 | return sum; 15 | } 16 | 17 | //单个的isolate 任务顺序执行 18 | final pool1 = IsolatePoolExecutor.newSingleIsolateExecutor(); 19 | 20 | //固定数量的isolate 任务按空闲分配 21 | final pool2 = IsolatePoolExecutor.newFixedIsolatePool(3); 22 | // 不限制总数 但空闲一段事件后如无新任务自动销毁 添加任务时没有空闲的isolate时创建新的isolate,有空闲的使用空闲的isolate 23 | final pool3 = IsolatePoolExecutor.newCachedIsolatePool(); 24 | 25 | //按需求自定义 26 | final pool4 = IsolatePoolExecutor( 27 | // 核心数量,无任务时一直等待,需要shutdown后才会销毁 28 | corePoolSize: 2, 29 | // 总数 超过核心数之后的空闲一段时间后会自动销毁 30 | maximumPoolSize: 4, 31 | //非核心的isolate空闲等待时间 32 | keepAliveTime: Duration(seconds: 1), 33 | // 任务寄放队列 34 | taskQueue: Queue(), 35 | // 当队列添加失败时的处理方式 36 | handler: RejectedExecutionHandler.abortPolicy); 37 | 38 | void doSomething() async { 39 | ///...doSomething 40 | // 用来提交任务 41 | final result = await pool1.compute(_doTask, 100000000); 42 | 43 | ///...doSomething 44 | } 45 | 46 | void willKillPool() { 47 | //终止池 48 | pool1.shutdown(); 49 | } 50 | 51 | 52 | 53 | ``` 54 | 55 | If you need to use MethodChannel in an isolate in Flutter, you can make the following adjustments. 56 | 57 | Note: This feature requires Flutter SDK >= 3.7. 58 | 59 | ```dart 60 | ///something 61 | //in mainIsolate 62 | RootIsolateToken rootIsolateToken = RootIsolateToken.instance!; 63 | 64 | final pool = IsolatePoolExecutor.newXXXXXPool( 65 | isolateValues: {'rootIsolateToken': rootIsolateToken}, 66 | onIsolateCreated: (isolateValues) { 67 | //in background isolate run 68 | final rootIsolateToken = isolateValues['rootIsolateToken']; 69 | BackgroundIsolateBinaryMessenger 70 | .ensureInitialized(rootIsolateToken); 71 | } 72 | ); 73 | 74 | ///...doSomething 75 | 76 | 77 | int? _doInBackgroundGetSharedPreferencesValue(String spKey) async { 78 | SharedPreferences sharedPreferences = await SharedPreferences.getInstance(); 79 | return sharedPreferences.getInt(spKey); 80 | } 81 | 82 | void doSomething() async { 83 | final result = await pool.compute(_doInBackgroundGetSharedPreferencesValue, 'spKey'); 84 | } 85 | 86 | ///...doSomething 87 | ``` 88 | 89 | If you encounter similar issues(https://github.com/flutter/flutter/issues/132731) in Flutter, you can refer to the following mitigation solutions. 90 | 91 | ```dart 92 | late final Future Function( 93 | FutureOr Function(M message) callback, M message, 94 | {String? debugLabel}) compute; 95 | 96 | void initPoolCompute() { 97 | final pool = Platform.isAndroid 98 | ? IsolatePoolExecutor( 99 | corePoolSize: 4, 100 | maximumPoolSize: 2147483647, 101 | keepAliveTime: const Duration(seconds: 30), 102 | //Start the 4 core isolates immediately. 103 | launchCoreImmediately: true, 104 | //If isolate creation times out twice consecutively, no new ones will be created, only the ones already started will be used. 105 | //If there's no timeout during creation, use the isolate containing the 4 cores and automatically destroy other cached isolates. 106 | onIsolateCreateTimeoutTimesDoNotCreateNew: 2, 107 | ) 108 | : IsolatePoolExecutor.newCachedIsolatePool( 109 | keepAliveTime: const Duration(seconds: 30)); 110 | 111 | compute = pool.compute; 112 | } 113 | 114 | void main() { 115 | // init pool 116 | initPoolCompute(); 117 | 118 | // ... doSomething 119 | compute((arg) { 120 | // ... doSomething 121 | }, 0); 122 | } 123 | 124 | ``` 125 | 126 | See [example](https://github.com/aymtools/isolate_pool_executor/blob/master/example/isolate_pool_executor_example.dart) 127 | for detailed test 128 | case. 129 | 130 | ## Issues 131 | 132 | If you encounter issues, here are some tips for debug, if nothing helps report 133 | to [issue tracker on GitHub](https://github.com/aymtools/isolate_pool_executor/issues): 134 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /example/isolate_pool_executor_example.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | import 'dart:isolate'; 3 | import 'dart:math'; 4 | 5 | import 'package:isolate_pool_executor/isolate_pool_executor.dart'; 6 | 7 | main() { 8 | final pool1 = IsolatePoolExecutor.newSingleIsolateExecutor(); 9 | 10 | pool1.compute(printName, 100000000); 11 | pool1.compute(printName, 100000001); 12 | pool1.compute(printName, 100000002); 13 | pool1.compute(printName, 100000003); 14 | pool1.compute(printName, 100000004); 15 | pool1.compute(printName, 100000005); 16 | pool1.compute(printName, 100000006); 17 | 18 | ///如果不执行shutdown 则会一直等待新任务 不会退出 19 | pool1.shutdown(); 20 | 21 | //将会如下打印 22 | // IsolatePoolExecutor-0-worker start input: 100000000 23 | // IsolatePoolExecutor-0-worker end input: 100000000 value: 499931747952 use time: 1551 24 | // IsolatePoolExecutor-0-worker start input: 100000001 25 | // IsolatePoolExecutor-0-worker end input: 100000001 value: 499945187362 use time: 1405 26 | // IsolatePoolExecutor-0-worker start input: 100000002 27 | // IsolatePoolExecutor-0-worker end input: 100000002 value: 499940162718 use time: 1400 28 | // IsolatePoolExecutor-0-worker start input: 100000003 29 | // IsolatePoolExecutor-0-worker end input: 100000003 value: 499907550879 use time: 1291 30 | // IsolatePoolExecutor-0-worker start input: 100000004 31 | // IsolatePoolExecutor-0-worker end input: 100000004 value: 499952207275 use time: 1362 32 | // IsolatePoolExecutor-0-worker start input: 100000005 33 | // IsolatePoolExecutor-0-worker end input: 100000005 value: 499985831624 use time: 1293 34 | // IsolatePoolExecutor-0-worker start input: 100000006 35 | // IsolatePoolExecutor-0-worker end input: 100000006 value: 499935347702 use time: 1125 36 | // 37 | // Process finished with exit code 0 38 | 39 | final pool2 = IsolatePoolExecutor.newFixedIsolatePool(3); 40 | pool2.compute(printName, 200000000); 41 | pool2.compute(printName, 200000001); 42 | pool2.compute(printName, 200000002); 43 | pool2.compute(printName, 200000003); 44 | pool2.compute(printName, 200000004); 45 | pool2.compute(printName, 200000005); 46 | pool2.compute(printName, 200000006); 47 | 48 | ///如果不执行shutdown 则会一直等待新任务 不会退出 49 | pool2.shutdown(); 50 | //其中某一次会如下打印 51 | // IsolatePoolExecutor-1-worker start input: 200000001 52 | // IsolatePoolExecutor-2-worker start input: 200000002 53 | // IsolatePoolExecutor-0-worker start input: 200000000 54 | // IsolatePoolExecutor-2-worker end input: 200000002 value: 999807824711 use time: 2167 55 | // IsolatePoolExecutor-2-worker start input: 200000003 56 | // IsolatePoolExecutor-0-worker end input: 200000000 value: 999798195612 use time: 3906 57 | // IsolatePoolExecutor-0-worker start input: 200000004 58 | // IsolatePoolExecutor-1-worker end input: 200000001 value: 999929274810 use time: 4079 59 | // IsolatePoolExecutor-1-worker start input: 200000005 60 | // IsolatePoolExecutor-2-worker end input: 200000003 value: 999871536520 use time: 2226 61 | // IsolatePoolExecutor-2-worker start input: 200000006 62 | // IsolatePoolExecutor-0-worker end input: 200000004 value: 999930512191 use time: 2198 63 | // IsolatePoolExecutor-1-worker end input: 200000005 value: 999890205891 use time: 3651 64 | // IsolatePoolExecutor-2-worker end input: 200000006 value: 999852440430 use time: 3919 65 | // 66 | // Process finished with exit code 0 67 | 68 | final pool3 = IsolatePoolExecutor.newCachedIsolatePool(); 69 | pool3.compute(printName, 500000000); 70 | pool3.compute(printName, 500000001); 71 | pool3.compute(printName, 500000002); 72 | pool3.compute(printName, 500000003); 73 | pool3.compute(printName, 500000004); 74 | pool3.compute(printName, 500000005); 75 | pool3.compute(printName, 500000006); 76 | 77 | // 非必须 任务队列空后会自动退出所有的后台isolate 78 | //pool3.shutdown(); 79 | //其中某一次会如下打印 80 | // IsolatePoolExecutor-3-worker start input: 500000003 81 | // IsolatePoolExecutor-4-worker start input: 500000004 82 | // IsolatePoolExecutor-6-worker start input: 500000006 83 | // IsolatePoolExecutor-5-worker start input: 500000005 84 | // IsolatePoolExecutor-0-worker start input: 500000000 85 | // IsolatePoolExecutor-1-worker start input: 500000001 86 | // IsolatePoolExecutor-2-worker start input: 500000002 87 | // IsolatePoolExecutor-6-worker end input: 500000006 value: 2499721241141 use time: 6502 88 | // IsolatePoolExecutor-1-worker end input: 500000001 value: 2499721459535 use time: 10303 89 | // IsolatePoolExecutor-4-worker end input: 500000004 value: 2499851277264 use time: 10989 90 | // IsolatePoolExecutor-2-worker end input: 500000002 value: 2499672191358 use time: 11025 91 | // IsolatePoolExecutor-5-worker end input: 500000005 value: 2499756041921 use time: 11083 92 | // IsolatePoolExecutor-3-worker end input: 500000003 value: 2499844333942 use time: 11244 93 | // IsolatePoolExecutor-0-worker end input: 500000000 value: 2499679184934 use time: 13071 94 | // 95 | // Process finished with exit code 0 96 | 97 | final pool4 = IsolatePoolExecutor( 98 | corePoolSize: 2, 99 | maximumPoolSize: 4, 100 | keepAliveTime: Duration(seconds: 1), 101 | taskQueue: Queue(), 102 | handler: RejectedExecutionHandler.abortPolicy); 103 | pool4.compute(printName, 500000000); 104 | pool4.compute(printName, 500000001); 105 | pool4.compute(printName, 500000002); 106 | pool4.compute(printName, 500000003); 107 | pool4.compute(printName, 500000004); 108 | pool4.compute(printName, 500000005); 109 | pool4.compute(printName, 500000006); 110 | pool4.shutdown(); 111 | } 112 | 113 | int printName(int n) { 114 | int startTime = DateTime.now().millisecondsSinceEpoch; 115 | print(Isolate.current.debugName! + ' start input: $n '); 116 | final v = _doTask(n); 117 | int endTime = DateTime.now().millisecondsSinceEpoch; 118 | print(Isolate.current.debugName! + 119 | ' end input: $n value: $v use time: ${endTime - startTime}'); 120 | return v; 121 | } 122 | 123 | int _doTask(int count) { 124 | int sum = 0; 125 | final random = new Random(); 126 | for (int i = 0; i < count; i++) { 127 | sum += random.nextInt(10000); 128 | } 129 | return sum; 130 | } 131 | 132 | Future testAwait() async { 133 | final pool3 = IsolatePoolExecutor.newCachedIsolatePool(); 134 | int i = await pool3.compute((message) => 1 * 5, 1); 135 | print('await $i'); 136 | return i; 137 | } 138 | -------------------------------------------------------------------------------- /lib/isolate_pool_executor.dart: -------------------------------------------------------------------------------- 1 | library isolate_pool_executor; 2 | 3 | export 'src/ext/isolate_pool_executor_ext.dart'; 4 | export 'src/isolate_pool_executor.dart'; 5 | export 'src/queue/queue_empty.dart'; 6 | export 'src/queue/queue_unique.dart'; 7 | export 'src/queue/queue_weighted.dart'; 8 | -------------------------------------------------------------------------------- /lib/src/ext/isolate_pool_executor_ext.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:isolate'; 3 | 4 | import '../isolate_pool_executor.dart'; 5 | 6 | extension IsolatePoolExecutorExt on IsolatePoolExecutor { 7 | /// 别名 8 | TaskFuture execute( 9 | FutureOr Function(Q message) callback, Q message, 10 | {String? debugLabel, int what = 0, dynamic tag, String? taskLabel}) => 11 | compute(callback, message, 12 | debugLabel: debugLabel, what: what, tag: tag, taskLabel: taskLabel); 13 | } 14 | 15 | Isolate get currentIsolate => Isolate.current; 16 | -------------------------------------------------------------------------------- /lib/src/future/task_future.dart: -------------------------------------------------------------------------------- 1 | part of '../isolate_pool_executor.dart'; 2 | 3 | class TaskFuture implements Future { 4 | final Future source; 5 | final ITask _task; 6 | 7 | TaskFuture._(this._task) : source = _task._future; 8 | 9 | int get taskId => _task.taskId; 10 | 11 | dynamic get tag => _task.tag; 12 | 13 | int get what => _task.what; 14 | 15 | @override 16 | Stream asStream() => source.asStream(); 17 | 18 | @override 19 | Future catchError(Function onError, {bool Function(Object error)? test}) => 20 | source.catchError(onError, test: test); 21 | 22 | @override 23 | Future then(FutureOr Function(T value) onValue, 24 | {Function? onError}) => 25 | source.then(onValue, onError: onError); 26 | 27 | @override 28 | Future timeout(Duration timeLimit, {FutureOr Function()? onTimeout}) => 29 | source.timeout(timeLimit, onTimeout: onTimeout); 30 | 31 | @override 32 | Future whenComplete(FutureOr Function() action) => 33 | source.whenComplete(action); 34 | } 35 | -------------------------------------------------------------------------------- /lib/src/isolate_pool_executor.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:collection'; 3 | import 'dart:isolate'; 4 | import 'dart:math'; 5 | 6 | import 'package:isolate_pool_executor/src/queue/queue_empty.dart'; 7 | 8 | part 'future/task_future.dart'; 9 | part 'isolate_pool_executor_cache.dart'; 10 | part 'isolate_pool_executor_core.dart'; 11 | part 'isolate_pool_executor_single.dart'; 12 | part 'isolate_pool_task.dart'; 13 | 14 | ///饱和策略,当阻塞队列满了,且没有空闲的工作线程,如果继续提交任务,必须采取一种策略处理该任务,提供4种策略: 15 | enum RejectedExecutionHandler { 16 | ///直接抛出异常,默认策略; 17 | abortPolicy, 18 | 19 | ///用调用者所在的Isolate来执行任务; 20 | callerRunsPolicy, 21 | 22 | ///丢弃阻塞队列中靠最前的任务; 23 | discardOldestPolicy, 24 | 25 | ///直接丢弃任务; 26 | discardPolicy, 27 | } 28 | 29 | /// 兼容旧版本 未来移除 30 | TaskInvoker? _warpTaskInvoker( 31 | Future Function( 32 | int taskId, FutureOr Function(dynamic) function, dynamic message)? 33 | invoker) { 34 | if (invoker == null) return null; 35 | return (taskId, function, message, _, __, ___) => 36 | invoker(taskId, function, message); 37 | } 38 | 39 | /// 池化的isolate 40 | abstract class IsolatePoolExecutor { 41 | /// [launchCoreImmediately] 是否立即启动所有的核心isolate 42 | /// 如果值为false时 同时判断[immediatelyStartedCore] 自定义的启动数量,不超过[corePoolSize] 43 | factory IsolatePoolExecutor({ 44 | required int corePoolSize, 45 | required int maximumPoolSize, 46 | Duration? keepAliveTime, 47 | Queue? taskQueue, 48 | RejectedExecutionHandler? handler, 49 | Map? isolateValues, 50 | bool launchCoreImmediately = false, 51 | int immediatelyStartedCore = 0, 52 | FutureOr Function(Map isolateValues)? 53 | onIsolateCreated, 54 | int onIsolateCreateTimeoutTimesDoNotCreateNew = 0, 55 | @Deprecated('Use customizeTaskInvoker, deprecated after v2.1.0 ') 56 | Future Function( 57 | int taskId, FutureOr Function(dynamic) function, dynamic message)? 58 | customTaskInvoker, 59 | TaskInvoker? customizeTaskInvoker, 60 | String? debugLabel, 61 | }) { 62 | assert(maximumPoolSize >= corePoolSize); 63 | return _IsolatePoolExecutorCore( 64 | corePoolSize: corePoolSize, 65 | maximumPoolSize: maximumPoolSize, 66 | keepAliveTime: const Duration(seconds: 30), 67 | taskQueue: taskQueue ?? Queue(), 68 | handler: handler ?? RejectedExecutionHandler.abortPolicy, 69 | isolateValues: isolateValues, 70 | launchCoreImmediately: launchCoreImmediately, 71 | immediatelyStartedCore: immediatelyStartedCore, 72 | onIsolateCreated: onIsolateCreated, 73 | onIsolateCreateTimeoutTimesDoNotCreateNew: 74 | onIsolateCreateTimeoutTimesDoNotCreateNew, 75 | customizeTaskInvoker: 76 | customizeTaskInvoker ?? _warpTaskInvoker(customTaskInvoker), 77 | debugLabel: debugLabel, 78 | ); 79 | } 80 | 81 | /// [launchCoreImmediately] 是否立即启动所有的核心isolate 82 | /// 如果值为false时 同时判断[immediatelyStartedCore] 自定义的启动数量,不超过[nIsolates] 83 | factory IsolatePoolExecutor.newFixedIsolatePool( 84 | int nIsolates, { 85 | Queue? taskQueue, 86 | RejectedExecutionHandler? handler, 87 | Map? isolateValues, 88 | bool launchCoreImmediately = false, 89 | int immediatelyStartedCore = 0, 90 | FutureOr Function(Map isolateValues)? 91 | onIsolateCreated, 92 | int onIsolateCreateTimeoutTimesDoNotCreateNew = 0, 93 | @Deprecated('Use taskInvoker, deprecated after v2.1.0 ') 94 | Future Function( 95 | int taskId, FutureOr Function(dynamic) function, dynamic message)? 96 | customTaskInvoker, 97 | TaskInvoker? customizeTaskInvoker, 98 | String? debugLabel, 99 | }) => 100 | _IsolatePoolExecutorCore( 101 | corePoolSize: nIsolates, 102 | maximumPoolSize: nIsolates, 103 | keepAliveTime: const Duration(seconds: 1), 104 | taskQueue: taskQueue ?? Queue(), 105 | handler: handler ?? RejectedExecutionHandler.abortPolicy, 106 | isolateValues: isolateValues, 107 | launchCoreImmediately: launchCoreImmediately, 108 | immediatelyStartedCore: immediatelyStartedCore, 109 | onIsolateCreated: onIsolateCreated, 110 | onIsolateCreateTimeoutTimesDoNotCreateNew: 111 | onIsolateCreateTimeoutTimesDoNotCreateNew, 112 | customizeTaskInvoker: 113 | customizeTaskInvoker ?? _warpTaskInvoker(customTaskInvoker), 114 | debugLabel: debugLabel, 115 | ); 116 | 117 | /// 118 | /// taskQueueInIsolate 为true时 taskQueueFactory 会跨isolate访问无法使用当前isolate中的数据 119 | factory IsolatePoolExecutor.newSingleIsolateExecutor({ 120 | bool taskQueueInIsolate = false, 121 | Queue Function()? taskQueueFactory, 122 | RejectedExecutionHandler? handler, 123 | Map? isolateValues, 124 | bool launchCoreImmediately = false, 125 | FutureOr Function(Map isolateValues)? 126 | onIsolateCreated, 127 | @Deprecated('Use customizeTaskInvoker, deprecated after v2.1.0 ') 128 | Future Function( 129 | int taskId, FutureOr Function(dynamic) function, dynamic message)? 130 | customTaskInvoker, 131 | TaskInvoker? customizeTaskInvoker, 132 | String? debugLabel, 133 | }) => 134 | taskQueueInIsolate 135 | ? _IsolatePoolSingleExecutor( 136 | taskQueueFactory: taskQueueFactory, 137 | handler: handler ?? RejectedExecutionHandler.abortPolicy, 138 | isolateValues: isolateValues, 139 | launchCoreImmediately: launchCoreImmediately, 140 | onIsolateCreated: onIsolateCreated, 141 | customizeTaskInvoker: 142 | customizeTaskInvoker ?? _warpTaskInvoker(customTaskInvoker), 143 | debugLabel: debugLabel, 144 | ) 145 | : IsolatePoolExecutor.newFixedIsolatePool( 146 | 1, 147 | taskQueue: taskQueueFactory?.call(), 148 | handler: handler, 149 | isolateValues: isolateValues, 150 | launchCoreImmediately: launchCoreImmediately, 151 | onIsolateCreated: onIsolateCreated, 152 | customizeTaskInvoker: 153 | customizeTaskInvoker ?? _warpTaskInvoker(customTaskInvoker), 154 | debugLabel: debugLabel, 155 | ); 156 | 157 | /// 创建一个无上限数量的缓存可用pool 158 | factory IsolatePoolExecutor.newCachedIsolatePool({ 159 | Duration keepAliveTime = const Duration(seconds: 10), 160 | Map? isolateValues, 161 | FutureOr Function(Map isolateValues)? 162 | onIsolateCreated, 163 | @Deprecated('Use taskInvoker, deprecated after v2.1.0 ') 164 | Future Function( 165 | int taskId, FutureOr Function(dynamic) function, dynamic message)? 166 | customTaskInvoker, 167 | TaskInvoker? customizeTaskInvoker, 168 | String? debugLabel, 169 | }) => 170 | _IsolatePoolExecutorCore( 171 | corePoolSize: 0, 172 | // java中int最大值 魔法数 173 | maximumPoolSize: 2147483647, 174 | keepAliveTime: keepAliveTime, 175 | taskQueue: QueueEmpty(), 176 | handler: RejectedExecutionHandler.abortPolicy, 177 | isolateValues: isolateValues, 178 | onIsolateCreated: onIsolateCreated, 179 | customizeTaskInvoker: 180 | customizeTaskInvoker ?? _warpTaskInvoker(customTaskInvoker), 181 | debugLabel: debugLabel, 182 | ); 183 | 184 | /// 提交一个任务 185 | /// [message] 和 [tag] 将会被发送到isolate中 注意是否可以发送 186 | /// https://api.dart.dev/dart-isolate/SendPort/send.html 187 | TaskFuture compute( 188 | FutureOr Function(Q message) callback, Q message, 189 | {String? debugLabel, int what = 0, dynamic tag, String? taskLabel}); 190 | 191 | /// 关闭当前的pool 192 | void shutdown({bool force = false}); 193 | 194 | /// 是否正在处于关闭中或这正在关闭 195 | bool get isShutdown; 196 | } 197 | 198 | final Map _isolateValues = {}; 199 | 200 | /// 用以存放到isolate中数据 201 | extension IsolateDataExt on Isolate { 202 | /// 获取存放在isolate中的数据 203 | dynamic operator [](Object? key) => _isolateValues[key]; 204 | } 205 | 206 | /// 自定义对于task的执行器 207 | typedef TaskInvoker = Future Function( 208 | int taskId, 209 | FutureOr Function(dynamic) function, 210 | dynamic message, 211 | String taskLabel, 212 | int what, 213 | dynamic tag); 214 | 215 | /// 当策略为RejectedExecutionHandler.abortPolicy 且无法添加到Queue时 的异常 216 | class RejectedExecutionException implements Exception { 217 | final dynamic error; 218 | 219 | RejectedExecutionException(this.error); 220 | 221 | @override 222 | String toString() { 223 | return 'RejectedExecutionException $error'; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /lib/src/isolate_pool_executor_cache.dart: -------------------------------------------------------------------------------- 1 | part of 'isolate_pool_executor.dart'; 2 | 3 | extension _IsolatePoolExecutorCoreNoCache on _IsolatePoolExecutorCore { 4 | _IsolateExecutor _makeNoCacheExecutor(ITask task) { 5 | final receivePort = RawReceivePort(); 6 | String? debugLabel; 7 | assert(() { 8 | debugLabel = 9 | 'IsolatePoolExecutor-NotCache${this.debugLabel?.isNotEmpty == true ? '-${this.debugLabel}' : ''}' 10 | '-${_isolateIndex++}'; 11 | return true; 12 | }()); 13 | 14 | _IsolateExecutor executor = _IsolateExecutor(receivePort, task, debugLabel); 15 | 16 | void runIsolate(_Task task) { 17 | final args = List.filled(5, null); 18 | args[0] = receivePort.sendPort; 19 | args[1] = task; 20 | args[2] = isolateValues; 21 | args[3] = onIsolateCreated; 22 | args[4] = customizeTaskInvoker; 23 | 24 | Isolate.spawn(_workerNoCache, args, 25 | onError: receivePort.sendPort, 26 | onExit: receivePort.sendPort, 27 | debugName: debugLabel) 28 | .then( 29 | (value) => executor.isolate = value, 30 | onError: (error, stackTrace) { 31 | final task = executor.close(); 32 | if (task != null) { 33 | task._submitError(error, stackTrace); 34 | } 35 | }, 36 | ); 37 | } 38 | 39 | receivePort.handler = ((message) { 40 | if (message == null) { 41 | // onExit handler message, isolate terminated without sending result. 42 | executor._task?._submitError( 43 | RemoteError("Computation ended without result", ""), 44 | StackTrace.empty); 45 | } else if (message is _Task) { 46 | runIsolate(message); 47 | return; 48 | } else if (message is _TaskResult) { 49 | executor.submit(message); 50 | } else if (message is List && message.length == 2) { 51 | //发生了异常退出 52 | final task = executor.close(); 53 | if (task != null) { 54 | var remoteError = message[0]; 55 | var remoteStack = message[1]; 56 | if (remoteStack is StackTrace) { 57 | // Typed error. 58 | task._submitError(remoteError!, remoteStack); 59 | } else { 60 | // onError handler message, uncaught async error. 61 | // Both values are strings, so calling `toString` is efficient. 62 | var error = RemoteError( 63 | remoteError.toString(), remoteStack?.toString() ?? ''); 64 | task._submitError(error, error.stackTrace); 65 | } 66 | } 67 | } else if (message is SendPort) { 68 | executor.sendPort = message; 69 | return; 70 | } 71 | executor.close(); 72 | _poolTask(); 73 | }); 74 | runIsolate(task._task!); 75 | return executor; 76 | } 77 | } 78 | 79 | void _workerNoCache(List args) { 80 | SendPort sendPort = args[0]; 81 | _runIsolateWorkGuarded(sendPort, () async { 82 | final isolateValues = args[2]; 83 | if (isolateValues != null) { 84 | _isolateValues.addAll(isolateValues as Map); 85 | } 86 | 87 | ReceivePort receivePort = ReceivePort(); 88 | sendPort.send(receivePort.sendPort); 89 | 90 | FutureOr Function(Map isolateValues)? 91 | onIsolateCreated = args[3]; 92 | if (onIsolateCreated != null) { 93 | final result = onIsolateCreated.call(_isolateValues); 94 | if (result is Future) { 95 | await result; 96 | } 97 | } 98 | 99 | _Task task = args[1]; 100 | 101 | final TaskInvoker? customInvoker = args[4]; 102 | if (customInvoker != null) { 103 | _taskInvoker = customInvoker; 104 | } 105 | 106 | _invokeTask(task).then((result) => Isolate.exit(sendPort, result)); 107 | }); 108 | } 109 | -------------------------------------------------------------------------------- /lib/src/isolate_pool_executor_core.dart: -------------------------------------------------------------------------------- 1 | part of 'isolate_pool_executor.dart'; 2 | 3 | class _IsolatePoolExecutorCore implements IsolatePoolExecutor { 4 | ///池中的核心线程数,当提交一个任务时,创建一个新的Isolate执行任务,直到当前Isolate数等于corePoolSize, 即使有其他空闲Isolate能够执行新来的任务, 也会继续创建Isolate;如果当前Isolate数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行 5 | final int corePoolSize; 6 | 7 | ///Isolate池中允许的最大Isolate数。如果当前阻塞队列满了,且继续提交任务,则创建新的Isolate执行任务,前提是当前Isolate数小于maximumPoolSize;当阻塞队列是无界队列, 则maximumPoolSize则不起作用, 因为无法提交至核心Isolate池的线程会一直持续地放入taskQueue. 8 | final int maximumPoolSize; 9 | final int cachePoolSize; 10 | 11 | ///Isolate空闲时的存活时间,即当Isolate没有任务执行时,该Isolate继续存活的时间;该参数只在Isolate数大于corePoolSize时才有用, 超过这个时间的空闲线程将被终止; 12 | final Duration keepAliveTime; 13 | 14 | ///用来保存等待被执行的任务的阻塞队列 15 | final Queue taskQueue; 16 | 17 | ///Isolate池的饱和策略,当阻塞队列满了,且没有空闲的工作Isolate,如果继续提交任务,必须采取一种策略处理该任务 18 | final RejectedExecutionHandler handler; 19 | final Map? isolateValues; 20 | 21 | final List<_IsolateExecutor?> _coreExecutor; 22 | final List<_IsolateExecutor> _cacheExecutor; 23 | final FutureOr Function(Map isolateValues)? 24 | onIsolateCreated; 25 | 26 | final String? debugLabel; 27 | 28 | final int onIsolateCreateTimeoutTimesDoNotCreateNew; 29 | 30 | bool _shutdown = false; 31 | bool _canCreateNewIsolate = true; 32 | 33 | int _isolateCreateTimeoutCounter = 0; 34 | int _isolateIndex = 0; 35 | 36 | final TaskInvoker? customizeTaskInvoker; 37 | 38 | _IsolatePoolExecutorCore( 39 | {required this.corePoolSize, 40 | required this.maximumPoolSize, 41 | required this.keepAliveTime, 42 | required this.taskQueue, 43 | required this.handler, 44 | this.isolateValues, 45 | bool launchCoreImmediately = false, 46 | this.onIsolateCreated, 47 | int immediatelyStartedCore = 0, 48 | int onIsolateCreateTimeoutTimesDoNotCreateNew = -1, 49 | this.customizeTaskInvoker, 50 | this.debugLabel}) 51 | : _coreExecutor = List.filled(corePoolSize, null), 52 | cachePoolSize = maximumPoolSize - corePoolSize, 53 | _cacheExecutor = [], 54 | onIsolateCreateTimeoutTimesDoNotCreateNew = 55 | onIsolateCreateTimeoutTimesDoNotCreateNew > 0 56 | ? onIsolateCreateTimeoutTimesDoNotCreateNew 57 | : -1, 58 | assert(maximumPoolSize >= corePoolSize, 59 | 'must maximumPoolSize >= corePoolSize') { 60 | final immediatelyStarted = launchCoreImmediately 61 | ? corePoolSize 62 | : min(immediatelyStartedCore, corePoolSize); 63 | 64 | if (immediatelyStarted > 0) { 65 | for (int i = 0; i < immediatelyStarted; i++) { 66 | final executor = _makeExecutor(true, null); 67 | executor.whenClose = () => _coreExecutor[i] = null; 68 | _coreExecutor[i] = executor; 69 | } 70 | } 71 | } 72 | 73 | @override 74 | TaskFuture compute( 75 | FutureOr Function(Q message) callback, Q message, 76 | {String? debugLabel, int what = 0, dynamic tag, String? taskLabel}) { 77 | debugLabel ??= callback.toString(); 78 | final task = _makeTask( 79 | (d) => callback(d), message, taskLabel ?? debugLabel, what, tag); 80 | return TaskFuture._(task); 81 | } 82 | 83 | void shutdown({bool force = false}) { 84 | _shutdown = true; 85 | if (force) { 86 | try { 87 | taskQueue.clear(); 88 | for (var e in _coreExecutor) { 89 | e?.close(); 90 | } 91 | } catch (_) {} 92 | } else if (taskQueue.isEmpty) { 93 | try { 94 | for (var e in _coreExecutor) { 95 | e?.close(); 96 | } 97 | } catch (_) {} 98 | } 99 | } 100 | 101 | ITask _makeTask(dynamic Function(dynamic p) run, dynamic p, 102 | String taskLabel, int what, dynamic tag) { 103 | if (_shutdown) { 104 | throw 'IsolatePoolExecutor${this.debugLabel?.isNotEmpty == true ? '-${this.debugLabel}' : ''} is shutdown'; 105 | } 106 | 107 | ITask task = ITask._task(run, p, taskLabel, what, tag); 108 | 109 | _addTask(task); 110 | 111 | return task; 112 | } 113 | 114 | void _addTask(ITask task, {bool header = false}) { 115 | final executor = _emitTask(task); 116 | if (executor != null) { 117 | // executor.emit(task); 118 | return; 119 | } 120 | try { 121 | if (header) { 122 | taskQueue.addFirst(task); 123 | } else { 124 | taskQueue.add(task); 125 | } 126 | _poolTask(); 127 | } catch (error) { 128 | switch (handler) { 129 | case RejectedExecutionHandler.abortPolicy: 130 | // 直接通知 task执行异常 131 | task._computer.completeError( 132 | RejectedExecutionException(error), StackTrace.current); 133 | break; 134 | case RejectedExecutionHandler.callerRunsPolicy: 135 | _runTask(task); 136 | break; 137 | case RejectedExecutionHandler.discardOldestPolicy: 138 | if (taskQueue.isNotEmpty) { 139 | taskQueue.removeFirst(); 140 | _addTask(task, header: header); 141 | } 142 | // 如果已经是空的还添加不进去意味着将自己也扔掉 143 | break; 144 | case RejectedExecutionHandler.discardPolicy: 145 | break; 146 | } 147 | } 148 | } 149 | 150 | _runTask(ITask taskX) async { 151 | final task = taskX._task; 152 | if (task == null) return; 153 | final taskResult = task.makeResult(); 154 | runZonedGuarded(() async { 155 | final invoker = customizeTaskInvoker ?? _taskInvoker; 156 | dynamic r = invoker(task.taskId, task.function, task.message, 157 | task.taskLabel, task.what, task.tag); 158 | if (r is Future) { 159 | r = await r; 160 | } 161 | taskResult.result = r; 162 | taskX._submit(taskResult); 163 | }, (error, stack) { 164 | taskResult.err = error; 165 | taskResult.stackTrace = stack; 166 | taskX._submit(taskResult); 167 | }); 168 | } 169 | 170 | void _poolTask([_IsolateExecutor? executorIdle]) { 171 | scheduleMicrotask(() { 172 | if (executorIdle != null && executorIdle.isIdle && taskQueue.isNotEmpty) { 173 | final task = taskQueue.removeFirst(); 174 | executorIdle.emit(task); 175 | return; 176 | } 177 | 178 | while (taskQueue.isNotEmpty) { 179 | final task = taskQueue.first; 180 | final executor = _emitTask(task); 181 | if (executor == null) { 182 | break; 183 | } 184 | final taskF = taskQueue.removeFirst(); 185 | assert(task == taskF, '??????'); 186 | } 187 | }); 188 | } 189 | 190 | _isolateCreateSupervisor(_IsolateExecutor executor) { 191 | if (onIsolateCreateTimeoutTimesDoNotCreateNew > 0) { 192 | executor.onTimeout = () { 193 | _isolateCreateTimeoutCounter++; 194 | if (_isolateCreateTimeoutCounter >= 195 | onIsolateCreateTimeoutTimesDoNotCreateNew) { 196 | _canCreateNewIsolate = false; 197 | } 198 | }; 199 | } 200 | } 201 | 202 | _IsolateExecutor? _emitTask(ITask task, 203 | {bool jumpCheckCanCreateNewIsolate = false}) { 204 | int i = 0, j = corePoolSize; 205 | for (; i < j; i++) { 206 | final e = _coreExecutor[i]; 207 | if (e == null) { 208 | if (_canCreateNewIsolate || jumpCheckCanCreateNewIsolate) { 209 | _IsolateExecutor executor = _makeExecutor(true, task); 210 | _coreExecutor[i] = executor; 211 | executor.whenClose = () => _coreExecutor[i] = null; 212 | return executor; 213 | } 214 | } else if (e.isIdle) { 215 | e.emit(task); 216 | return e; 217 | } 218 | } 219 | if (cachePoolSize == 0) return null; 220 | final idleExecutor = _cacheExecutor.firstWhereOrNull((e) => e.isIdle); 221 | if (idleExecutor != null) { 222 | idleExecutor.emit(task); 223 | return idleExecutor; 224 | } 225 | if (_cacheExecutor.length == cachePoolSize) { 226 | return null; 227 | } 228 | if (keepAliveTime == Duration.zero) { 229 | return _makeNoCacheExecutor(task); 230 | } else if (_canCreateNewIsolate || jumpCheckCanCreateNewIsolate) { 231 | _IsolateExecutor executor = _makeExecutor(false, task); 232 | executor.whenClose = () => _cacheExecutor.remove(executor); 233 | _cacheExecutor.add(executor); 234 | return executor; 235 | } 236 | if (!jumpCheckCanCreateNewIsolate) { 237 | if (!_canCreateNewIsolate && 238 | (_coreExecutor.isEmpty && _cacheExecutor.isEmpty)) { 239 | //当前无法创建新isolate,并且也没有可用的isolate时 那就继续尝试创建 240 | return _emitTask(task, jumpCheckCanCreateNewIsolate: true); 241 | } 242 | } 243 | 244 | return null; 245 | // _IsolateExecutor executor = keepAliveTime == Duration.zero 246 | // ? _makeNoCacheExecutor(task) 247 | // : _makeExecutor(false, task); 248 | // executor.whenClose = () => _cacheExecutor.remove(executor); 249 | // _cacheExecutor.add(executor); 250 | // return executor; 251 | } 252 | 253 | _IsolateExecutor _makeExecutor(bool isCore, ITask? task) { 254 | // final completer = Completer(); 255 | final receivePort = RawReceivePort(); 256 | String? debugLabel; 257 | assert(() { 258 | debugLabel = 'IsolatePoolExecutor-${isCore ? 'Core' : 'NotCore'}' 259 | '${this.debugLabel?.isNotEmpty == true ? '-${this.debugLabel}' : ''}' 260 | '-${_isolateIndex++}'; 261 | return true; 262 | }()); 263 | 264 | _IsolateExecutor executor = _IsolateExecutor(receivePort, task, debugLabel); 265 | 266 | _isolateCreateSupervisor(executor); 267 | 268 | receivePort.handler = ((message) { 269 | if (message == null) { 270 | //执行了Isolate exit 271 | final task = executor.close(); 272 | 273 | if (task != null) { 274 | //执行推出时存在待执行的task时重新发回头部执行 275 | _addTask(task, header: true); 276 | } 277 | return; 278 | } else if (message is SendPort) { 279 | executor.sendPort = message; 280 | 281 | _canCreateNewIsolate = true; 282 | _isolateCreateTimeoutCounter = 0; 283 | 284 | if (isCore && executor.isIdle) { 285 | _poolTask(executor); 286 | } 287 | return; 288 | } else if (message is _TaskResult) { 289 | executor.submit(message); 290 | 291 | if (_shutdown && isCore && taskQueue.isEmpty) { 292 | executor.close(); 293 | return; 294 | } 295 | _poolTask(executor); 296 | } else if (message is List && message.length == 2) { 297 | //发生了异常退出 298 | final task = executor.close(); 299 | if (task != null) { 300 | var remoteError = message[0]; 301 | var remoteStack = message[1]; 302 | if (remoteStack is StackTrace) { 303 | // Typed error. 304 | task._submitError(remoteError!, remoteStack); 305 | } else { 306 | // onError handler message, uncaught async error. 307 | // Both values are strings, so calling `toString` is efficient. 308 | var error = RemoteError( 309 | remoteError.toString(), remoteStack?.toString() ?? ''); 310 | task._submitError(error, error.stackTrace); 311 | } 312 | } 313 | _poolTask(); 314 | } 315 | }); 316 | 317 | final args = List.filled(6, null); 318 | args[0] = receivePort.sendPort; 319 | if (!isCore) args[1] = keepAliveTime; 320 | args[2] = isolateValues; 321 | args[3] = task?._task; 322 | args[4] = onIsolateCreated; 323 | args[5] = customizeTaskInvoker; 324 | 325 | Isolate.spawn(_worker, args, 326 | onError: receivePort.sendPort, 327 | onExit: receivePort.sendPort, 328 | debugName: debugLabel) 329 | .then( 330 | (value) => executor.isolate = value, 331 | onError: (error, stackTrace) { 332 | final task = executor.close(); 333 | if (task != null) { 334 | task._submitError(error, stackTrace); 335 | } 336 | }, 337 | ); 338 | 339 | return executor; 340 | } 341 | 342 | @override 343 | bool get isShutdown => _shutdown; 344 | } 345 | 346 | void _worker(List args) { 347 | SendPort sendPort = args[0]; 348 | _runIsolateWorkGuarded(sendPort, () async { 349 | Duration? duration = args[1]; 350 | final isolateValues = args[2]; 351 | 352 | if (isolateValues != null) { 353 | _isolateValues.addAll(isolateValues as Map); 354 | } 355 | 356 | ReceivePort receivePort = ReceivePort(); 357 | sendPort.send(receivePort.sendPort); 358 | 359 | FutureOr Function(Map isolateValues)? 360 | onIsolateCreated = args[4]; 361 | if (onIsolateCreated != null) { 362 | final result = onIsolateCreated.call(_isolateValues); 363 | if (result is Future) { 364 | await result; 365 | } 366 | } 367 | 368 | final _Task? task = args[3]; 369 | 370 | final TaskInvoker? customInvoker = args[5]; 371 | if (customInvoker != null) { 372 | _taskInvoker = customInvoker; 373 | } 374 | 375 | void Function() startListen; 376 | if (duration == null) { 377 | startListen = () => receivePort 378 | .listen((message) async => sendPort.send(await _invokeTask(message))); 379 | } else { 380 | startListen = () { 381 | final exitDuration = duration; 382 | Timer exitTimer = Timer(exitDuration, () => Isolate.exit()); 383 | receivePort.listen((message) async { 384 | exitTimer.cancel(); 385 | try { 386 | final result = await _invokeTask(message); 387 | sendPort.send(result); 388 | } finally { 389 | exitTimer.cancel(); 390 | exitTimer = Timer(exitDuration, () => Isolate.exit()); 391 | } 392 | }); 393 | }; 394 | } 395 | 396 | if (task != null) { 397 | _invokeTask(task).then(sendPort.send).then((_) => startListen()); 398 | } else { 399 | startListen(); 400 | } 401 | }); 402 | } 403 | -------------------------------------------------------------------------------- /lib/src/isolate_pool_executor_single.dart: -------------------------------------------------------------------------------- 1 | part of 'isolate_pool_executor.dart'; 2 | 3 | // Queue _defaultTaskQueueFactory() => Queue(); 4 | 5 | class _IsolatePoolSingleExecutor implements IsolatePoolExecutor { 6 | final Queue Function()? taskQueueFactory; 7 | 8 | final Map? isolateValues; 9 | final List<_IsolateExecutor?> _coreExecutor = List.filled(1, null); 10 | 11 | final Map taskQueue = {}; 12 | 13 | final RejectedExecutionHandler? handler; 14 | 15 | final FutureOr Function(Map isolateValues)? 16 | onIsolateCreated; 17 | 18 | final TaskInvoker? customizeTaskInvoker; 19 | 20 | final String? debugLabel; 21 | 22 | bool _shutdown = false; 23 | 24 | List creatingCache = []; 25 | 26 | late final void Function(ITask task, int what, dynamic tag) _emitTask = 27 | taskQueueFactory == null ? _emitTask2 : _emitTask1; 28 | 29 | _IsolatePoolSingleExecutor( 30 | {Queue Function()? taskQueueFactory, 31 | this.handler, 32 | this.isolateValues, 33 | bool launchCoreImmediately = false, 34 | this.onIsolateCreated, 35 | this.customizeTaskInvoker, 36 | this.debugLabel}) 37 | : taskQueueFactory = taskQueueFactory { 38 | assert( 39 | taskQueueFactory == null || 40 | handler != RejectedExecutionHandler.callerRunsPolicy, 41 | 'When the queue is within an Isolate, it cannot be executed in the calling Isolate.'); 42 | 43 | if (launchCoreImmediately) { 44 | final executor = _makeExecutor(null); 45 | executor.whenClose = () => _coreExecutor[0] = null; 46 | _coreExecutor[0] = executor; 47 | } 48 | } 49 | 50 | @override 51 | TaskFuture compute( 52 | FutureOr Function(Q message) callback, Q message, 53 | {String? debugLabel, int what = 0, dynamic tag, String? taskLabel}) { 54 | debugLabel ??= callback.toString(); 55 | final task = _makeTask( 56 | (d) => callback(d), message, taskLabel ?? debugLabel, what, tag); 57 | return TaskFuture._(task); 58 | } 59 | 60 | @override 61 | void shutdown({bool force = false}) { 62 | _shutdown = true; 63 | if (force) { 64 | try { 65 | _coreExecutor[0]?.close(); 66 | _coreExecutor[0] = null; 67 | } catch (_) {} 68 | } 69 | } 70 | 71 | ITask _makeTask(dynamic Function(dynamic p) run, dynamic p, 72 | String taskLabel, int what, dynamic tag) { 73 | if (_shutdown) { 74 | throw 'SingleIsolatePoolExecutor${this.debugLabel?.isNotEmpty == true ? '-${this.debugLabel}' : ''} is shutdown'; 75 | } 76 | 77 | ITask task = ITask._task(run, p, taskLabel, what, tag); 78 | taskQueue[task.taskId] = task; 79 | 80 | _emitTask(task, what, tag); 81 | return task; 82 | } 83 | 84 | void _emitTask1(ITask task, int what, dynamic tag) { 85 | final t = task._task; 86 | var executor = _coreExecutor[0]; 87 | if (executor == null) { 88 | executor = _makeExecutor(task); 89 | _coreExecutor[0] = executor; 90 | executor.whenClose = () => _coreExecutor[0] = null; 91 | task._task = null; 92 | } else if (executor.isCreating) { 93 | creatingCache.add(task); 94 | } else if (!executor.isClosed) { 95 | try { 96 | executor._sendPort!.send(t); 97 | task._task = null; 98 | } catch (err, st) { 99 | task._submitError(err, st); 100 | executor.close(); 101 | } 102 | } else {} 103 | } 104 | 105 | void _emitTask2(ITask task, int what, dynamic tag) { 106 | final t = task._task; 107 | 108 | var executor = _coreExecutor[0]; 109 | if (executor == null) { 110 | executor = _makeExecutor(task); 111 | _coreExecutor[0] = executor; 112 | executor.whenClose = () => _coreExecutor[0] = null; 113 | task._task = null; 114 | } else if (executor.isCreating) { 115 | creatingCache.add(task); 116 | } else if (!executor.isClosed) { 117 | try { 118 | executor._sendPort!.send(t); 119 | task._task = null; 120 | } catch (err, st) { 121 | task._submitError(err, st); 122 | executor.close(); 123 | } 124 | } else {} 125 | } 126 | 127 | _IsolateExecutor _makeExecutor(ITask? fistTask) { 128 | final receivePort = RawReceivePort(); 129 | 130 | String? debugLabel; 131 | 132 | assert(() { 133 | debugLabel = 134 | 'IsolatePoolExecutor-Single${this.debugLabel?.isNotEmpty == true ? '-${this.debugLabel}' : ''}'; 135 | return true; 136 | }()); 137 | 138 | _IsolateExecutor executor = 139 | _IsolateExecutor(receivePort, fistTask, debugLabel); 140 | 141 | //需要特殊处理 142 | executor.onTimeout = () { 143 | var error = 144 | "Create Isolate timeout \n https://github.com/flutter/flutter/issues/132731"; 145 | taskQueue.values.forEach((task) { 146 | task._submitError(error, StackTrace.empty); 147 | }); 148 | executor.close(); 149 | taskQueue.clear(); 150 | creatingCache.clear(); 151 | }; 152 | 153 | receivePort.handler = ((message) { 154 | if (message == null) { 155 | //执行了Isolate exit 156 | final err = RemoteError("Computation ended without result", ""); 157 | taskQueue.values.forEach((task) { 158 | task._submitError(err, StackTrace.empty); 159 | }); 160 | executor.close(); 161 | taskQueue.clear(); 162 | creatingCache.clear(); 163 | } else if (message is SendPort) { 164 | // if (!completer.isCompleted) completer.complete(message); 165 | executor.sendPort = message; 166 | if (taskQueueFactory == null) { 167 | creatingCache.forEach((task) { 168 | message.send(task._task!); 169 | task._task = null; 170 | }); 171 | } else { 172 | creatingCache.forEach((task) { 173 | final t = task._task; 174 | final msg = List.filled(3, null); 175 | msg[0] = t; 176 | msg[1] = task.what; 177 | msg[2] = task.tag; 178 | message.send(msg); 179 | task._task = null; 180 | }); 181 | } 182 | creatingCache.clear(); 183 | return; 184 | } else if (message is _TaskResult) { 185 | _TaskResult result = message; 186 | taskQueue[result.taskId]?._submit(result); 187 | taskQueue.remove(result.taskId); 188 | if (_shutdown && taskQueue.isEmpty) { 189 | executor.close(); 190 | } 191 | } else if (message is List && message.length == 2) { 192 | //发生了异常退出 193 | var remoteError = message[0]; 194 | var remoteStack = message[1]; 195 | if (remoteStack! is StackTrace) { 196 | var error = RemoteError( 197 | remoteError.toString(), remoteStack?.toString() ?? ''); 198 | remoteError = error; 199 | remoteStack = error.stackTrace; 200 | } 201 | 202 | taskQueue.values.forEach((task) { 203 | task._submitError(remoteError, remoteStack); 204 | }); 205 | executor.close(); 206 | taskQueue.clear(); 207 | creatingCache.clear(); 208 | } 209 | }); 210 | final args = List.filled(7, null); 211 | args[0] = receivePort.sendPort; 212 | args[1] = taskQueueFactory; 213 | args[2] = isolateValues; 214 | args[3] = fistTask?._task; 215 | args[4] = onIsolateCreated; 216 | args[5] = handler; 217 | args[6] = customizeTaskInvoker; 218 | 219 | Isolate.spawn(_workerSingle, args, 220 | onError: receivePort.sendPort, 221 | onExit: receivePort.sendPort, 222 | debugName: debugLabel) 223 | .then((value) { 224 | executor.isolate = value; 225 | }).catchError( 226 | (error, stackTrace) { 227 | executor.close(); 228 | taskQueue.values.forEach((task) { 229 | task._submitError(error, stackTrace); 230 | }); 231 | executor.close(); 232 | taskQueue.clear(); 233 | creatingCache.clear(); 234 | }, 235 | ); 236 | 237 | return executor; 238 | } 239 | 240 | @override 241 | bool get isShutdown => _shutdown; 242 | } 243 | 244 | void _workerSingle(List args) { 245 | SendPort sendPort = args[0]; 246 | _runIsolateWorkGuarded(sendPort, () async { 247 | Queue Function()? taskQueueFactory = args[1]; 248 | 249 | final isolateValues = args[2]; 250 | if (isolateValues != null) { 251 | _isolateValues.addAll(isolateValues as Map); 252 | } 253 | 254 | ReceivePort receivePort = ReceivePort(); 255 | sendPort.send(receivePort.sendPort); 256 | 257 | FutureOr Function(Map isolateValues)? 258 | onIsolateCreated = args[4]; 259 | if (onIsolateCreated != null) { 260 | final result = onIsolateCreated.call(_isolateValues); 261 | if (result is Future) { 262 | await result; 263 | } 264 | } 265 | 266 | final _Task? task = args[3]; 267 | 268 | final RejectedExecutionHandler handler = 269 | args[5] ?? RejectedExecutionHandler.abortPolicy; 270 | 271 | final TaskInvoker? customInvoker = args[6]; 272 | if (customInvoker != null) { 273 | _taskInvoker = customInvoker; 274 | } 275 | 276 | void Function() startListen; 277 | 278 | if (taskQueueFactory == null) { 279 | startListen = () => receivePort 280 | .listen((message) async => sendPort.send(await _invokeTask(message))); 281 | } else { 282 | Queue taskQueue = taskQueueFactory(); 283 | _Task? doingTask; 284 | 285 | late void Function() _poolTask; 286 | 287 | void invokeTask(_Task task) async { 288 | final taskResult = await _invokeTask(task); 289 | sendPort.send(taskResult); 290 | doingTask = null; 291 | _poolTask(); 292 | } 293 | 294 | _poolTask = () { 295 | scheduleMicrotask(() { 296 | if (doingTask != null) return; 297 | while (doingTask == null && taskQueue.isNotEmpty) { 298 | final task = taskQueue.removeFirst(); 299 | doingTask = task._task; 300 | } 301 | if (doingTask != null) { 302 | Timer.run(() => invokeTask(doingTask!)); 303 | } 304 | }); 305 | }; 306 | void addTask(ITask task) { 307 | try { 308 | taskQueue.add(task); 309 | } catch (error) { 310 | switch (handler) { 311 | case RejectedExecutionHandler.abortPolicy: 312 | // 直接通知 task执行异常 313 | final errResult = task._task?.makeResult(); 314 | if (errResult != null) { 315 | errResult.err = RejectedExecutionException(error); 316 | errResult.stackTrace = StackTrace.current; 317 | sendPort.send(errResult); 318 | } 319 | break; 320 | case RejectedExecutionHandler.callerRunsPolicy: 321 | break; 322 | case RejectedExecutionHandler.discardOldestPolicy: 323 | if (taskQueue.isNotEmpty) { 324 | taskQueue.removeFirst(); 325 | addTask(task); 326 | } 327 | // 如果已经是空的还添加不进去意味着将自己也扔掉 328 | break; 329 | case RejectedExecutionHandler.discardPolicy: 330 | break; 331 | } 332 | } 333 | } 334 | 335 | startListen = () => receivePort.listen((message) { 336 | scheduleMicrotask(() { 337 | addTask(ITask._taskValue(message)); 338 | _poolTask(); 339 | }); 340 | }); 341 | } 342 | if (task != null) { 343 | _invokeTask(task).then(sendPort.send).then((_) => startListen()); 344 | } else { 345 | startListen(); 346 | } 347 | }); 348 | } 349 | -------------------------------------------------------------------------------- /lib/src/isolate_pool_task.dart: -------------------------------------------------------------------------------- 1 | part of 'isolate_pool_executor.dart'; 2 | 3 | int _currIndex = 0; 4 | 5 | const _maxIndex = 0xffffffffffff; 6 | 7 | int _nextTaskId() { 8 | _currIndex = (_currIndex + 1) & _maxIndex; 9 | return _currIndex; 10 | } 11 | 12 | ///待执行的任务 13 | class ITask { 14 | final int taskId; 15 | _Task? _task; 16 | 17 | final Completer _computer; 18 | final String taskLabel; 19 | final dynamic tag; 20 | final int what; 21 | 22 | /// 只有在任务提交前可以使用 提交后为了优化将会置为null 23 | dynamic get taskMessage => _task?.message; 24 | 25 | /// 一般用于配合队列 缓存 对task按一定规则计算排序时 计算的结果 26 | dynamic obj; 27 | 28 | ITask._task( 29 | FutureOr Function(dynamic q) function, 30 | dynamic message, 31 | this.taskLabel, 32 | this.what, 33 | this.tag, 34 | ) : taskId = _nextTaskId(), 35 | _computer = Completer() { 36 | _task = _Task(function, message, taskId, taskLabel, what, tag); 37 | } 38 | 39 | ITask._taskValue( 40 | _Task this._task, 41 | ) : taskId = _task.taskId, 42 | taskLabel = _task.taskLabel, 43 | what = _task.what, 44 | tag = _task.tag, 45 | _computer = Completer(); 46 | 47 | Future get _future => _computer.future; 48 | 49 | void _submit(_TaskResult result) { 50 | if (_computer.isCompleted) return; 51 | if (result.err == null) { 52 | _computer.complete(result.result); 53 | } else { 54 | _computer.completeError(result.err, result.stackTrace); 55 | } 56 | } 57 | 58 | void _submitError(Object error, [StackTrace? stackTrace]) { 59 | if (_computer.isCompleted) return; 60 | _computer.completeError(error, stackTrace); 61 | } 62 | } 63 | 64 | class _TaskResult { 65 | final int taskId; 66 | final String taskLabel; 67 | dynamic result; 68 | dynamic err; 69 | StackTrace? stackTrace; 70 | 71 | _TaskResult(this.taskLabel, this.taskId); 72 | } 73 | 74 | class _Task { 75 | final int taskId; 76 | final String taskLabel; 77 | final dynamic message; 78 | final FutureOr Function(dynamic q) function; 79 | final int what; 80 | final dynamic tag; 81 | 82 | _Task(this.function, this.message, this.taskId, this.taskLabel, this.what, 83 | this.tag); 84 | 85 | _TaskResult makeResult() => _TaskResult(taskLabel, taskId); 86 | } 87 | 88 | enum IsolateExecutorState { 89 | creating, 90 | idle, 91 | running, 92 | close, 93 | } 94 | 95 | int _creating = () { 96 | int t = 3; 97 | assert(() { 98 | t = 6; 99 | return true; 100 | }()); 101 | return t; 102 | }(); 103 | 104 | // Future get _waitOtherImmediatelyStarted async { 105 | // if (_creating < 5) { 106 | // return Future.value(); 107 | // } 108 | // await Future.delayed(Duration(seconds: 1)); 109 | // return _waitOtherImmediatelyStarted; 110 | // } 111 | 112 | class _IsolateExecutor { 113 | final RawReceivePort _receivePort; 114 | final String? debugLabel; 115 | void Function()? whenClose; 116 | bool _isClosed = false; 117 | Isolate? _isolate; 118 | SendPort? _sendPort; 119 | 120 | ITask? _task; 121 | 122 | late Timer timer; 123 | void Function()? onTimeout; 124 | 125 | _IsolateExecutor(this._receivePort, this._task, this.debugLabel); 126 | 127 | bool get isIdle => 128 | _isolate != null && _sendPort != null && !_isClosed && _task == null; 129 | 130 | bool get isClosed => _isClosed; 131 | 132 | bool get isCreating => !isClosed && _sendPort == null; 133 | 134 | set sendPort(SendPort port) { 135 | if (_creating > 3) _creating--; 136 | timer.cancel(); 137 | assert(!isClosed); 138 | _sendPort = port; 139 | } 140 | 141 | set isolate(Isolate isolate) { 142 | assert(!isClosed); 143 | _isolate = isolate; 144 | _creating++; 145 | final time = _creating; 146 | //用以监听此bug https://github.com/flutter/flutter/issues/132731 147 | timer = Timer(Duration(seconds: _creating), () { 148 | if (_creating > 3) _creating--; 149 | if (!isClosed && _sendPort == null) { 150 | final errStr = CreateIsolateTimeoutException( 151 | debugLabel: debugLabel, waitTime: time); 152 | 153 | close()?._submitError(errStr, StackTrace.empty); 154 | print(errStr); 155 | onTimeout?.call(); 156 | } 157 | }); 158 | } 159 | 160 | void emit(ITask task) { 161 | if (_task == task) return; 162 | this._task = task; 163 | assert(!isIdle, 'IsolateExecutor is busy'); 164 | try { 165 | _sendPort!.send(task._task); 166 | task._task = null; 167 | } catch (err, st) { 168 | task._submitError(err, st); 169 | close(); 170 | } 171 | } 172 | 173 | void submit(_TaskResult result) { 174 | if (result.taskId == _task?.taskId) { 175 | _task?._submit(result); 176 | } 177 | _task = null; 178 | } 179 | 180 | ITask? close() { 181 | _isClosed = true; 182 | _sendPort = null; 183 | final t = _task; 184 | whenClose?.call(); 185 | _task = null; 186 | _receivePort.close(); 187 | _isolate?.kill(); 188 | whenClose = null; 189 | return t?._task == null ? null : t; 190 | } 191 | } 192 | 193 | TaskInvoker _taskInvoker = (taskId, function, message, _, __, ___) async { 194 | dynamic result = function(message); 195 | if (result is Future) { 196 | result = await result; 197 | } 198 | return result; 199 | }; 200 | 201 | Future<_TaskResult> _invokeTask(_Task task) async { 202 | final taskResult = task.makeResult(); 203 | Completer<_TaskResult> computer = Completer(); 204 | runZonedGuarded(() async { 205 | dynamic r = _taskInvoker(task.taskId, task.function, task.message, 206 | task.taskLabel, task.what, task.tag); 207 | if (r is Future) { 208 | r = await r; 209 | } 210 | taskResult.result = r; 211 | if (!computer.isCompleted) computer.complete(taskResult); 212 | }, (error, stack) { 213 | taskResult.err = error; 214 | taskResult.stackTrace = stack; 215 | if (!computer.isCompleted) computer.complete(taskResult); 216 | }); 217 | return computer.future; 218 | } 219 | 220 | void _runIsolateWorkGuarded(SendPort sendPort, void Function() block) { 221 | runZonedGuarded(block, (error, stack) { 222 | final errs = List.filled(2, null); 223 | errs[0] = error; 224 | errs[1] = stack; 225 | Isolate.exit(sendPort, errs); 226 | }); 227 | } 228 | 229 | extension _ListFirstWhereOrNullExt on List { 230 | E? firstWhereOrNull(bool test(E element)) { 231 | for (E element in this) { 232 | if (test(element)) return element; 233 | } 234 | return null; 235 | } 236 | } 237 | 238 | class CreateIsolateTimeoutException implements Exception { 239 | final String? debugLabel; 240 | 241 | final int waitTime; 242 | 243 | CreateIsolateTimeoutException( 244 | {required this.debugLabel, required this.waitTime}); 245 | 246 | String toString() { 247 | return "Create Isolate(${debugLabel ?? ''}) timeout (wait $waitTime seconds)\n " 248 | "Known cause:\n1.Open too many isolates at the same time \n2.https://github.com/flutter/flutter/issues/132731"; 249 | } 250 | } 251 | -------------------------------------------------------------------------------- /lib/src/queue/queue_empty.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | /** The always empty iterator. */ 4 | class EmptyIterator implements Iterator { 5 | const EmptyIterator(); 6 | 7 | bool moveNext() => false; 8 | 9 | E get current { 10 | throw IterableElementError.noElement(); 11 | } 12 | } 13 | 14 | /** 15 | * The always empty Queue. 16 | */ 17 | class QueueEmpty implements Queue { 18 | @override 19 | void add(E value) { 20 | throw IterableElementError.tooMany(); 21 | } 22 | 23 | @override 24 | void addAll(Iterable iterable) { 25 | throw IterableElementError.tooMany(); 26 | } 27 | 28 | @override 29 | void addFirst(E value) { 30 | throw IterableElementError.tooMany(); 31 | } 32 | 33 | @override 34 | void addLast(E value) { 35 | throw IterableElementError.tooMany(); 36 | } 37 | 38 | @override 39 | bool any(bool Function(E element) test) { 40 | return false; 41 | } 42 | 43 | @override 44 | Queue cast() { 45 | return QueueEmpty(); 46 | } 47 | 48 | @override 49 | void clear() {} 50 | 51 | @override 52 | bool contains(Object? element) { 53 | return false; 54 | } 55 | 56 | @override 57 | E elementAt(int index) { 58 | throw IterableElementError.noElement(); 59 | } 60 | 61 | @override 62 | bool every(bool Function(E element) test) { 63 | return false; 64 | } 65 | 66 | @override 67 | Iterable expand(Iterable Function(E element) toElements) { 68 | return Iterable.empty(); 69 | } 70 | 71 | @override 72 | E get first => throw IterableElementError.noElement(); 73 | 74 | @override 75 | E firstWhere(bool Function(E element) test, {E Function()? orElse}) { 76 | throw IterableElementError.noElement(); 77 | } 78 | 79 | @override 80 | T fold(T initialValue, T Function(T previousValue, E element) combine) { 81 | return initialValue; 82 | } 83 | 84 | @override 85 | Iterable followedBy(Iterable other) { 86 | return Iterable.empty(); 87 | } 88 | 89 | @override 90 | void forEach(void Function(E element) action) {} 91 | 92 | @override 93 | bool get isEmpty => true; 94 | 95 | @override 96 | bool get isNotEmpty => false; 97 | 98 | @override 99 | Iterator get iterator => EmptyIterator(); 100 | 101 | @override 102 | String join([String separator = ""]) { 103 | return ''; 104 | } 105 | 106 | @override 107 | E get last => throw IterableElementError.noElement(); 108 | 109 | @override 110 | E lastWhere(bool Function(E element) test, {E Function()? orElse}) { 111 | throw IterableElementError.noElement(); 112 | } 113 | 114 | @override 115 | int get length => 0; 116 | 117 | @override 118 | Iterable map(T Function(E e) toElement) { 119 | return Iterable.empty(); 120 | } 121 | 122 | @override 123 | E reduce(E Function(E value, E element) combine) { 124 | throw IterableElementError.noElement(); 125 | } 126 | 127 | @override 128 | bool remove(Object? value) { 129 | return false; 130 | } 131 | 132 | @override 133 | E removeFirst() { 134 | throw IterableElementError.noElement(); 135 | } 136 | 137 | @override 138 | E removeLast() { 139 | throw IterableElementError.noElement(); 140 | } 141 | 142 | @override 143 | void removeWhere(bool Function(E element) test) {} 144 | 145 | @override 146 | void retainWhere(bool Function(E element) test) {} 147 | 148 | @override 149 | E get single => throw IterableElementError.noElement(); 150 | 151 | @override 152 | E singleWhere(bool Function(E element) test, {E Function()? orElse}) { 153 | throw IterableElementError.noElement(); 154 | } 155 | 156 | @override 157 | Iterable skip(int count) { 158 | return Iterable.empty(); 159 | } 160 | 161 | @override 162 | Iterable skipWhile(bool Function(E value) test) { 163 | return Iterable.empty(); 164 | } 165 | 166 | @override 167 | Iterable take(int count) { 168 | return Iterable.empty(); 169 | } 170 | 171 | @override 172 | Iterable takeWhile(bool Function(E value) test) { 173 | return Iterable.empty(); 174 | } 175 | 176 | @override 177 | List toList({bool growable = true}) { 178 | return List.empty(growable: growable); 179 | } 180 | 181 | @override 182 | Set toSet() { 183 | return {}; 184 | } 185 | 186 | @override 187 | Iterable where(bool Function(E element) test) { 188 | return Iterable.empty(); 189 | } 190 | 191 | @override 192 | Iterable whereType() { 193 | return Iterable.empty(); 194 | } 195 | } 196 | 197 | /** 198 | * Creates errors throw by [Iterable] when the element count is wrong. 199 | */ 200 | abstract class IterableElementError { 201 | /** Error thrown thrown by, e.g., [Iterable.first] when there is no result. */ 202 | static StateError noElement() => StateError("No element"); 203 | 204 | /** Error thrown by, e.g., [Iterable.single] if there are too many results. */ 205 | static StateError tooMany() => StateError("Too many elements"); 206 | 207 | /** Error thrown by, e.g., [List.setRange] if there are too few elements. */ 208 | static StateError tooFew() => StateError("Too few elements"); 209 | } 210 | -------------------------------------------------------------------------------- /lib/src/queue/queue_unique.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | /// 可以根据规则 自动去重的队列 4 | class UniqueQueue extends IterableBase implements Queue { 5 | final List _items = []; 6 | final Set _seen = {}; 7 | 8 | final Object Function(T) _genUnique; 9 | 10 | UniqueQueue(this._genUnique); 11 | 12 | @override 13 | void addFirst(T value) { 14 | final uniqueProperty = _genUnique(value); 15 | if (_seen.add(uniqueProperty)) { 16 | _items.insert(0, value); 17 | } 18 | } 19 | 20 | @override 21 | void addLast(T value) { 22 | final uniqueProperty = _genUnique(value); 23 | if (_seen.add(uniqueProperty)) { 24 | _items.add(value); 25 | } 26 | } 27 | 28 | @override 29 | T removeFirst() { 30 | if (_items.isNotEmpty) { 31 | final item = _items.removeAt(0); 32 | _seen.remove(_genUnique(item)); 33 | return item; 34 | } 35 | throw StateError('Queue is empty'); 36 | } 37 | 38 | @override 39 | T removeLast() { 40 | if (_items.isNotEmpty) { 41 | final item = _items.removeAt(_items.length - 1); 42 | _seen.remove(_genUnique(item)); 43 | return item; 44 | } 45 | throw StateError('Queue is empty'); 46 | } 47 | 48 | @override 49 | bool get isEmpty => _items.isEmpty; 50 | 51 | @override 52 | int get length => _items.length; 53 | 54 | @override 55 | Iterator get iterator => _items.iterator; 56 | 57 | @override 58 | void clear() { 59 | _items.clear(); 60 | _seen.clear(); 61 | } 62 | 63 | @override 64 | bool remove(Object? value) { 65 | if (_items.remove(value)) { 66 | if (value is T) { 67 | _seen.remove(_genUnique(value)); 68 | } 69 | return true; 70 | } 71 | return false; 72 | } 73 | 74 | @override 75 | void forEach(void Function(T element) f) { 76 | _items.forEach(f); 77 | } 78 | 79 | @override 80 | bool contains(Object? element) => _items.contains(element); 81 | 82 | @override 83 | void removeWhere(bool Function(T element) test) { 84 | _items.removeWhere(test); 85 | _seen.clear(); 86 | for (var item in _items) { 87 | _seen.add(_genUnique(item)); 88 | } 89 | } 90 | 91 | @override 92 | void add(T value) { 93 | addLast(value); 94 | } 95 | 96 | @override 97 | void addAll(Iterable iterable) { 98 | for (var item in iterable) { 99 | add(item); 100 | } 101 | } 102 | 103 | @override 104 | void retainWhere(bool Function(T element) test) { 105 | _items.retainWhere(test); 106 | _seen.clear(); 107 | for (var item in _items) { 108 | _seen.add(_genUnique(item)); 109 | } 110 | } 111 | 112 | @override 113 | Queue cast() { 114 | return Queue.of(_items.cast()); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /lib/src/queue/queue_weighted.dart: -------------------------------------------------------------------------------- 1 | import 'dart:collection'; 2 | 3 | /// 可以自定义权重的队列 4 | class WeightedQueue extends IterableBase implements Queue { 5 | final List _items = []; 6 | 7 | final int Function(T) _genWeighted; 8 | 9 | WeightedQueue(this._genWeighted); 10 | 11 | @override 12 | void addFirst(T value) { 13 | addLast(value); 14 | } 15 | 16 | @override 17 | void addLast(T value) { 18 | final weight = _genWeighted(value); 19 | _items.add(value); 20 | _items.sort((a, b) => _genWeighted(b).compareTo(weight)); // 从高到低排序 21 | } 22 | 23 | @override 24 | T removeFirst() { 25 | if (_items.isNotEmpty) { 26 | return _items.removeAt(0); // 移除并返回权重最高的元素 27 | } 28 | throw StateError('Queue is empty'); 29 | } 30 | 31 | @override 32 | T removeLast() { 33 | if (_items.isNotEmpty) { 34 | return _items.removeAt(_items.length - 1); 35 | } 36 | throw StateError('Queue is empty'); 37 | } 38 | 39 | @override 40 | bool get isEmpty => _items.isEmpty; 41 | 42 | @override 43 | int get length => _items.length; 44 | 45 | @override 46 | Iterator get iterator => _items.iterator; 47 | 48 | @override 49 | void clear() { 50 | _items.clear(); 51 | } 52 | 53 | @override 54 | bool remove(Object? value) { 55 | if (_items.remove(value)) { 56 | return true; 57 | } 58 | return false; 59 | } 60 | 61 | @override 62 | void forEach(void Function(T element) f) { 63 | _items.forEach(f); 64 | } 65 | 66 | @override 67 | bool contains(Object? element) => _items.contains(element); 68 | 69 | @override 70 | void removeWhere(bool Function(T element) test) { 71 | _items.removeWhere(test); 72 | } 73 | 74 | @override 75 | void add(T value) { 76 | addLast(value); 77 | } 78 | 79 | @override 80 | void addAll(Iterable iterable) { 81 | _items.addAll(iterable); 82 | _items.sort((a, b) => _genWeighted(b).compareTo(_genWeighted(a))); // 从高到低排序 83 | } 84 | 85 | @override 86 | void retainWhere(bool Function(T element) test) { 87 | _items.retainWhere(test); 88 | } 89 | 90 | @override 91 | Queue cast() { 92 | return Queue.of(_items.cast()); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: isolate_pool_executor 2 | description: Just like a thread pool, but with isolates. The Isolate Pool Executor is a tool in Dart for managing concurrent tasks by limiting and reusing isolates to optimize performance. 3 | version: 2.1.0 4 | homepage: https://github.com/aymtools/isolate_pool_executor 5 | topics: 6 | - isolate 7 | - isolate-pool 8 | - pool 9 | - thread 10 | - thread-pool 11 | 12 | environment: 13 | sdk: '>=2.15.0 <4.0.0' --------------------------------------------------------------------------------