Dart:我必须取消 Stream 订阅并关闭 StreamSinks 吗?

Posted

技术标签:

【中文标题】Dart:我必须取消 Stream 订阅并关闭 StreamSinks 吗?【英文标题】:Dart: Do I have to cancel Stream subscriptions and close StreamSinks? 【发布时间】:2018-09-27 11:59:02 【问题描述】:

我知道当我不想再接收任何事件时,我必须取消 Stream 订阅。 即使在收到“完成”事件后,我也必须这样做吗?还是会出现内存泄漏?

传递给另一个 Stream 的 addStream 的 Stream 会发生什么情况?他们会自动取消吗?

StreamSink 方面的相同问题如果流已经完成,我是否必须关闭它们?

【问题讨论】:

【参考方案1】:

简短回答:不,但你应该StreamSubscriptionStreamSink 的合同中没有任何内容要求关闭资源,但是如果您不关闭它们,某些用例可能会导致内存泄漏,即使在某些情况下这样做也是如此可能会令人困惑。这些类的部分混淆在于它们被重载,并且处理两个相当不同的用例:

    资源流(如文件 I/O、网络访问) 事件流(如点击处理程序)

让我们一次一个地解决这些问题,首先,StreamSubscription

StreamSubscription

当您将listen 发送到Stream 时,您会收到StreamSubscription。一般来说,当您完成收听Stream 时,无论出于何种原因,您都应该关闭订阅。如果选择不这样做,并非所有流都会泄漏内存,但有些流会泄漏 - 例如,如果您正在从文件中读取输入,则不关闭流意味着文件的句柄可能保持打开状态。

所以,虽然不是严格要求,但我会总是在访问完流时cancel

StreamSink

StreamSink 最常见的实现是StreamController,它是创建Stream 的编程接口。通常,当您的流完成(即所有数据已发出)时,您应该关闭控制器。

这里有点令人困惑。让我们看看这两种情况:

文件 I/O

假设您正在创建一个 API 来逐行异步读取文件:

Stream<String> readLines(String path);

要实现这一点,您可以使用StreamController

Stream<String> readLines(String path) 
  SomeFileResource someResource;
  StreamController<String> controller;
  controller = new StreamController<String>(
    onListen: () 
      someResource = new SomeFileResource(path);
      // TODO: Implement adding to the controller.
    ,
  );
  return controller.stream;

在这种情况下,当读完最后一行时关闭controller 会很有意义。这会向用户发出信号(done 事件),表明文件已被读取,并且是有意义的(例如,您可以在那时关闭 File 资源)。

活动

假设您正在创建一个 API 来收听 HackerNews 上的新闻文章:

Stream<String> readHackerNews();

在这里,关闭底层接收器/控制器的意义不大。 HackerNews 会停止吗?像这样的事件流(或 UI 程序中的 click 处理程序)传统上不会在没有用户访问的情况下“停止”(即取消 StreamSubscription)。

可以在完成后关闭控制器,但这不是必需的。


希望这是有道理的,可以帮助你!

【讨论】:

谢谢!只有您的简短回答与您以后的阐述不一致。但它让我现在有了更好的理解。非常感谢 我认为答案确实是“是的,你应该”,而“不,你没有必须”在技术上是正确的(如详细说明由剩余的文本)。也就是说,在某些情况下您不需要这样做,但总是自己清理会更容易(并将例外情况留给少数情况下不太容易并且你知道你不需要需要)。当您在订阅中看到 listencancel 匹配时,比查看代码却看不到任何对象生命周期的迹象更容易理解代码。 我认为 Dart 不允许重载。你这是什么意思?【参考方案2】:

我发现如果我有这样的代码:

Stream<String> readHackerNews(String path)   
  StreamController<String> controller = StreamController<String>();

  ......

  return controller.stream;

我看到一条警告消息“关闭 'dart.core.Sink' 的实例”。在 Visual Studio 代码中。 为了解决这个警告,我添加了

controller.close()

到 OnCancel 事件的事件处理程序,见下文:

Stream<String> readHackerNews(String path)   
  StreamController<String> controller = StreamController<String>();

  //TODO: your code here

  controller.onCancel = () 
    controller.close();
  ;

  return controller.stream;

希望这会有所帮助!

【讨论】:

以上是关于Dart:我必须取消 Stream 订阅并关闭 StreamSinks 吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Navigator.of(context).push(....) 之后临时取消订阅 Stream?

Dart 语言异步编程之Stream

谷歌云消息“未注册”失败并取消订阅最佳做法?

Angular RxJS Observable:takeUntil 与使用订阅取消订阅 [关闭]

Flutter状态管理——单Stream和广播Stream

Flutter状态管理——单Stream和广播Stream