gpt4 book ai didi

Dart - 检测未完成的 future

转载 作者:行者123 更新时间:2023-12-05 05:38:25 26 4
gpt4 key购买 nike

我有一个调用第三方库的 Dart 控制台应用程序。

当我的控制台应用程序调用第三方库时,对方法的调用返回,但是我的 CLI 应用程序随后“挂起”10 秒左右,然后最终关闭。

我怀疑图书馆有某种类型的资源,它已经创建但尚未关闭/完成。

我最好的猜测是这是一个未完成的 future 。

所以我正在寻找方法来检测尚未释放的资源。

我的第一个停靠点是寻找一种技术来检测尚未完成的 future ,但其他资源类型的解决方案会很有用。

我目前正在使用 runZoneGuarded,传入 ZoneSpecification 以 Hook 调用。

编辑:通过一些实验,我发现我可以检测计时器并取消它们。在一个简单的实验中,我发现未取消的计时器会导致应用程序挂起。如果我取消计时器(在我的 checkLeaks 方法期间),应用程序将关闭,但是,这在我的真实应用程序中还不够,所以我仍在寻找检测其他资源的方法。

这是我的实验代码:

#! /usr/bin/env dcli

import 'dart:async';

import 'package:dcli/dcli.dart';
import 'package:onepub/src/pub/global_packages.dart';
import 'package:onepub/src/pub/system_cache.dart';
import 'package:onepub/src/version/version.g.dart';
import 'package:pub_semver/pub_semver.dart';

void main(List<String> arguments) async {
print(orange('OnePub version: $packageVersion '));

print('');

print(globals);

// await globals.repairActivatedPackages();

await runZonedGuarded(() async {
Timer(Duration(seconds: 20), () => print('timer done'));
unawaited(Future.delayed(Duration(seconds: 20)));
var completer = Completer();
unawaited(
Future.delayed(Duration(seconds: 20), () => completer.complete()));

// await globals.activateHosted(
// 'dcli_unit_tester',
// VersionConstraint.any,
// null, // all executables
// overwriteBinStubs: true,
// url: null, // hostedUrl,
// );
print('end activate');
}, (error, stackTrace) {
print('Uncaught error: $error');
}, zoneSpecification: buildZoneSpec());

print('end');

checkLeaks();

// await entrypoint(arguments, CommandSet.ONEPUB, 'onepub');
}

late final SystemCache cache = SystemCache(isOffline: false);
GlobalPackages? _globals;
GlobalPackages get globals => _globals ??= GlobalPackages(cache);

List<void Function()> actions = [];
List<Source<Timer>> timers = [];

int testCounter = 0;
int timerCount = 0;
int periodicCallbacksCount = 0;
int microtasksCount = 0;

ZoneSpecification buildZoneSpec() {
return ZoneSpecification(
createTimer: (source, parent, zone, duration, f) {
timerCount += 1;
final result = parent.createTimer(zone, duration, f);
timers.add(Source(result));
return result;
},
createPeriodicTimer: (source, parent, zone, period, f) {
periodicCallbacksCount += 1;
final result = parent.createPeriodicTimer(zone, period, f);
timers.add(Source(result));
return result;
},
scheduleMicrotask: (source, parent, zone, f) {
microtasksCount += 1;
actions.add(f);
final result = parent.scheduleMicrotask(zone, f);
return result;
},
);
}

void checkLeaks() {
print(actions.length);
print(timers.length);

print('testCounter $testCounter');
print('timerCount $timerCount');
print('periodicCallbacksCount $periodicCallbacksCount');
print('microtasksCount $microtasksCount');

for (var timer in timers) {
if (timer.source.isActive) {
print('Active Timer: ${timer.st}');
timer.source.cancel();
}
}
}

class Source<T> {
Source(this.source) {
st = StackTrace.current;
}
T source;
late StackTrace st;
}

我是真实世界的测试,我可以看到我确实有由 HTTP 连接引起的挂起计时器。正如我最初猜测的那样,这似乎确实指出了 HTTP 连接未正确关闭的其他一些问题。

Active Timer: #0      new Source (file:///home/bsutton/git/onepub/onepub/bin/onepub.dart:105:21)
#1 buildZoneSpec.<anonymous closure> (file:///home/bsutton/git/onepub/onepub/bin/onepub.dart:68:18)
#2 _CustomZone.createTimer (dart:async/zone.dart:1388:19)
#3 new Timer (dart:async/timer.dart:54:10)
#4 _HttpClientConnection.startTimer (dart:_http/http_impl.dart:2320:18)
#5 _ConnectionTarget.returnConnection (dart:_http/http_impl.dart:2381:16)
#6 _HttpClient._returnConnection (dart:_http/http_impl.dart:2800:41)
#7 _HttpClientConnection.send.<anonymous closure>.<anonymous closure>.<anonymous closure> (dart:_http/http_impl.dart:2171:25)
#8 _rootRunUnary (dart:async/zone.dart:1434:47)

最佳答案

一般来说,不可能找到不会发生的事情。

无法在程序中找到所有的 future。

有了区域,您可能能够截取区域中“注册”的所有回调,但您无法知道必须调用其中的哪些回调。 Future 可以同时具有值​​处理程序和错误处理程序,并且最多会调用其中一个。因此,仅仅因为未调用 future 的回调,并不意味着 future 没有完成。

不过, future 很可能不会让这个孤立的人活着。如果没有重要的东西卡在上面,一个未完成的 future 将被垃圾收集。

保持隔离事件的最有可能的罪魁祸首是计时器和接收端口。(定时器、I/O 和套接字的 VM 内部实现都使用接收端口,所以它实际上只是端口。)

同样,无法以编程方式找到所有打开的端口。为此,您需要一个带有内存检查工具的调试器。

我建议使用 developer tools查找未被垃圾回收的 ReceivePortRawReceivePort 实例,并查看它们是否仍然存在。


同时注意 runZonedGuarded

由于 runZonedGuarded 引入了一个新的错误区域(因为它在新区域中引入了一个 Uncaught Error 处理程序),因此在该区域内部创建的 future 错误将不会在区域外完成。

这意味着代码:

await runZonedGuarded(() async {

如果 body 抛出将不起作用。 future 的错误由区域而不是 await 处理,因此 await 只会看到一个永远不会完成的 future 。

关于Dart - 检测未完成的 future ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/72917939/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com