鉴于前文是主isolate和一个子isolate间的通信,这里再给出个主isolate和多个isolate之间的通信例子,这个例子给出的是2个子isolate。
这里也记录为啥由单子isolate向多子isolate转变。原因:由于项目中主要耗时的任务分为两类,一个是数据库处理,另一个是http相关的处理,之前我偷懒,就把这两个都放到同一个子isolate中去处理。然后,我就遇到了主子isolate通信阻塞的问题。当然是我这边的锅,前文中的例子,主子isolate间的通信是主发给子,子处理完再发回给主,再关闭port,如果子isolate中发生了异常、卡死等的问题,则主无法再向子发出处理要求。因此可将这两者分开以减少两者间的影响。
注:
在主子isolate通信时,http返回的内容不能直接回传,会报错Invalid argument(s): Illegal argument in isolate message: object is unsendable - Library:'dart:async' Class: _ControllerStream@4048458 (see restrictions listed at SendPort.send() documentation for more information)
。
因为 SendPort.send() 对可发送的对象有限制,可发送的类型有null、num、bool、String、List、Map、SendPort
。
import 'dart:isolate';
import 'dart:convert';
import 'package:http/http.dart' as http;
// 模拟数据库操作
class DatabaseService {
static Future<dynamic> performDatabaseOperation(dynamic data) async {
// 这里可以实现具体的数据库操作
await Future.delayed(Duration(seconds: 1));
return 'Database operation result: $data';
}
}
// 处理数据库操作的子 Isolate 入口函数
void databaseIsolateEntry(SendPort mainSendPort) {
final receivePort = ReceivePort();
mainSendPort.send([receivePort.sendPort, 'dbIsolate']);
receivePort.listen((message) async {
final sendPort = message[0];
final data = message.sublist(1);
final result = await DatabaseService.performDatabaseOperation(data);
sendPort.send(result);
});
}
// 处理 HTTP 调用的子 Isolate 入口函数
void httpIsolateEntry(SendPort mainSendPort) {
final receivePort = ReceivePort();
mainSendPort.send([receivePort.sendPort, 'httpIsolate']);
receivePort.listen((message) async {
final sendPort = message[0];
final data = message.sublist(1);
try {
final response = await http.get(Uri.parse(data[0]));
if (response.statusCode == 200) {
sendPort.send(json.decode(response.body));
} else {
sendPort.send('HTTP request failed with status: ${response.statusCode}');
}
} catch (e) {
sendPort.send('Error making HTTP request: $e');
}
});
}
class IsolateService {
static Isolate? _databaseIsolate;
static Isolate? _httpIsolate;
static SendPort? _databaseSendPort;
static SendPort? _httpSendPort;
static final ReceivePort _receivePort = ReceivePort();
static Future<void> init() async {
_databaseIsolate = await Isolate.spawn(databaseIsolateEntry, _receivePort.sendPort);
_httpIsolate = await Isolate.spawn(httpIsolateEntry, _receivePort.sendPort);
int initializedCount = 0;
await for (var message in _receivePort) {
if (message[0] is SendPort && message[1] == "dbIsolate") {
_databaseSendPort = message[0];
initializedCount++;
} else if (message[0] is SendPort && message[1] == "httpIsolate") {
_httpSendPort = message[0];
initializedCount++;
}
if (initializedCount == 2) {
break;
}
}
}
static Future<dynamic> sendToDatabase(dynamic message) async {
if (_databaseSendPort == null) {
throw Exception('Database isolate is not initialized. Call init() first.');
}
final responseReceivePort = ReceivePort();
_databaseSendPort!.send([responseReceivePort.sendPort, ...message]);
try {
final response = await responseReceivePort.first;
print("mainIsolate has get the response from database isolate: $response");
responseReceivePort.close();
return response;
} catch (e) {
print('Error receiving response from database isolate: $e');
return null;
}
}
static Future<dynamic> sendToHttp(dynamic message) async {
if (_httpSendPort == null) {
throw Exception('HTTP isolate is not initialized. Call init() first.');
}
final responseReceivePort = ReceivePort();
_httpSendPort!.send([responseReceivePort.sendPort, ...message]);
try {
final response = await responseReceivePort.first;
print("mainIsolate has get the response from http isolate: $response");
responseReceivePort.close();
return response;
} catch (e) {
print('Error receiving response from http isolate: $e');
return null;
}
}
static void dispose() {
_receivePort.close();
_databaseIsolate?.kill(priority: Isolate.immediate);
_httpIsolate?.kill(priority: Isolate.immediate);
}
}
void main() async {
await IsolateService.init();
// 向数据库子 Isolate 发送消息
final databaseResult = await IsolateService.sendToDatabase(['test data']);
print('Database result: $databaseResult');
// 向 HTTP 子 Isolate 发送消息
final httpResult = await IsolateService.sendToHttp(['https://jsonplaceholder.typicode.com/todos/1']);
print('HTTP result: $httpResult');
IsolateService.dispose();
}