Asynchrone Aufgaben und parallele Ausführer (ähnlich Thread-Pools) für alle Dart-Plattformen (transparent), ohne sich mit der Komplexität von `Isolate` befassen zu müssen.
Aus öffentlichen Paketdaten erstellte Orientierung.
async_task ist ein Dart-Paket, das die Ausführung asynchroner Aufgaben und die Verwaltung paralleler Ausführung auf allen Plattformen – einschließlich Android, iOS, Web, macOS, Windows und Linux – vereinfacht. Es bietet eine Threadpool-ähnliche Abstraktion, ohne dass Entwickler die Komplexität von Isolates handhaben müssen. Das Paket gewährleistet eine transparente, plattformübergreifende Konkurrenz und erleichtert die effiziente Entlastung von Arbeit, während sauberer und lesbarer Code beibehalten wird. Ideal für Hintergrundverarbeitung, Datenabruf oder CPU-intensive Operationen.
Anwendungsfälle
Hintergrundverarbeitung
Parallele Aufgabenausführung
CPU-intensive Operationen
Datenabruf im Hintergrund
Plattformübergreifende Konkurrenz
Aufgabenplanung
Hauptmerkmale
plattformunabhängige Konkurrenz
Keine Isolate-Verwaltung erforderlich
Eingebaute parallele Ausführungsstrecken
Plattformübergreifend transparent
Einfache API für asynchrone Aufgaben
Unterstützt alle Dart-Plattformen
Geeignet für
Entwickler, die einfache Konkurrenz suchen
Apps, die Hintergrundarbeit benötigen
Plattformübergreifende Projekte
Teams, die die Isolate-Komplexität vermeiden
Leistungsstarke Anwendungen
Echtzeit-Datenverarbeitung
Hinweise
Nicht geeignet für langlaufende blockierende Aufgaben
Begrenzter Zugriff auf die zugrunde liegenden Threads
Kann eine sorgfältige Speicherverwaltung erfordern
Am besten für Aufgaben mit kurzer bis mittlerer Dauer
Abhängig vom Verhalten der Ereignisschleife von Dart
Vermeiden Sie verschachtelte asynchrone Aufgabenketten
Original-README
Originaler README-Schnappschuss aus dem verknüpften Quell-Repository. Aktuelle Inhalte finden Sie im Repository.
async_task
This package brings asynchronous tasks and parallel executors (similar to classic thread pools) for all Dart platforms
(JS/Web, Flutter, VM/Native) through transparent internal implementations, based on dart:isolate or
only dart:async, without having to deal with the Isolate complexity.
Motivation
Dart parallelism is based on asynchronous, non-blocking and thread-safe code. This creates a language that facilitates
the creation of safe concurrency code, but all running in the same thread, using only 1 thread/core of a device.
If you want to use more than 1 thread/core of a device, Dart VM/Native has Isolate, but the paradigm is not easy to
use like classical Thread Pools, or like environments that shares all the memory/objects between threads.
This package was created to facilitate the creation and execution of multiple tasks asynchronously in all Dart
Platforms, avoiding platform specific codes by the developer.
Usage
import'dart:async';
import'package:async_task/async_task.dart';
voidmain() async {
// The tasks to execute:var tasks = [
PrimeChecker(8779),
PrimeChecker(1046527),
PrimeChecker(3139581), // Not PrimePrimeChecker(16769023),
];
// Instantiate the task executor:var asyncExecutor = AsyncExecutor(
sequential: false, // Non-sequential tasks.
parallelism: 2, // Concurrency with 2 threads.
taskTypeRegister: _taskTypeRegister, // The top-level function to register the tasks types.
);
// Enable logging output:
asyncExecutor.logger.enabled = true ;
// Execute all tasks:var executions = asyncExecutor.executeAll(tasks);
// Wait tasks executions:awaitFuture.wait(executions);
for (var task in tasks) {
var n = task.n; // Number to check for prime.var prime = task.result; // Task result: true if is prime.print('$n\t-> $prime \t $task');
}
}
// This top-level function returns the tasks types that will be registered// for execution. Task instances are returned, but won't be executed and// will be used only to identify the task type:List<AsyncTask> _taskTypeRegister() => [PrimeChecker(0)];
// A task that checks if a number is prime:classPrimeCheckerextendsAsyncTask<int, bool> {
// The number to check if is prime.finalint n;
PrimeChecker(this.n);
// Instantiates a `PrimeChecker` task with `parameters`.@overrideAsyncTask<int, bool> instantiate(int parameters) {
returnPrimeChecker(parameters);
}
// The parameters of this task:@overrideintparameters() {
return n;
}
// Runs the task code:@overrideFutureOr<bool> run() {
returnisPrime(n);
}
// A simple prime check function:boolisPrime(int n) {
if (n < 2) returnfalse;
var limit = n ~/ 2;
for (var p = 2; p <= limit; ++p) {
if (n % p == 0) returnfalse;
}
returntrue;
}
}
Note that the parameter parallelism at AsyncExecutor shouldn't be greater than the CPU cores
(unless you really have a case that justifies a higher number).
The most common error is to put a high number for the parameter parallelism thinking that this will increase tasks
execution, but this actually can reduce your application performance. Note that each allocated Isolateneeds a CPU
core to execute, and a high number of Isolate instances running tasks will be a bottleneck in your system
(specially if you are demanding more Isolate than CPU cores).
SharedData
The class SharedData facilitates and optimizes data shared between tasks.
The main advantage of data encapsulated with SharedData is to avoid multiple
messages, with the same data, between threads/isolates, avoiding concurrency
performance issues and multiple duplicated objects in memory (a GC bottleneck).
Here's an example of a task using SharedData:
// A task that checks if a number is prime:classPrimeCheckerextendsAsyncTask<int, bool> {
// The number to check if is prime.finalint n;
// A list of known primes, shared between tasks.finalSharedData<List<int>, List<int>> knownPrimes;
PrimeChecker(this.n, this.knownPrimes);
// Instantiates a `PrimeChecker` task with `parameters` and `sharedData`.@overridePrimeCheckerinstantiate(int parameters, [Map<String, SharedData>? sharedData]) {
returnPrimeChecker(
parameters,
sharedData!['knownPrimes'] asSharedData<List<int>, List<int>>,
);
}
// The `SharedData` of this task.@overrideMap<String, SharedData> sharedData() => {'knownPrimes': knownPrimes};
// Loads the `SharedData` from `serial` for each key.@overrideSharedData<List<int>, List<int>> loadSharedData(String key, dynamic serial) {
switch (key) {
case'knownPrimes':
returnSharedData<List<int>, List<int>>(serial);
default:
throwStateError('Unknown key: $key');
}
}
// The parameters of this task:@overrideintparameters() {
return n;
}
// Runs the task code:@overrideFutureOr<bool> run() {
returnisPrime(n);
}
// A simple prime check function:boolisPrime(int n) {
if (n < 2) returnfalse;
// The pre-computed primes, optimizing this checking algorithm:if (knownPrimes.data.contains(n)) {
returntrue;
}
// It's sufficient to search for prime factors in the range [1,sqrt(N)]:var limit = (sqrt(n) + 1).toInt();
for (var p = 2; p < limit; ++p) {
if (n % p == 0) returnfalse;
}
returntrue;
}
}
The field knownPrimes above will be shared between tasks. In platforms with support
for dart:isolateknownPrimes will be sent through an Isolate port only once,
avoiding multiple copies and unnecessary memory allocations.
AsyncTaskChannel
An AsyncTask can have a communication channel that can be used to send/received messages
during task execution.
To use a task channel just override channelInstantiator, than use channelResolved() inside
the task and await channel() outside it:
classYourTaskextendsAsyncTask<String, int> {
// ...@overrideAsyncTaskChannel? channelInstantiator() => AsyncTaskChannel(); // You can extend `AsyncTaskChannel` class if needed.// ...@overrideFutureOr<int> run() async {
// ...// get the resolved channel:var channel = channelResolved()!; // The channel is always resolved inside `run()`.// Send and wait a message in the channel:
channel.send('some message');
var result = await channel.waitMessage<String>();
// Send and wait in a single method:var result = await channel.sendAndWaitResponse<String, String>('some message');
// Read a message if available in the queue (non-blocking):var msgOptional = channel.readMessage<String>();
// ...
}
}
Outside communication with the task:
voidmain() async {
// ...// Execute the task:
asyncExecutor.execute(task);
// ...// Wait for the channel to be resolved:var channel = (await task.channel())!;
// Wait for a message:var msg = await channel.waitMessage();
// process msg...// Send a response:
channel.send('Some response');
// ...
}
An AsyncTaskChannel is automatically closed when a task finishes (returns its result).