【问题标题】:How to change the dart-sqlite code from synchronous style to asynchronous?如何将 dart-sqlite 代码从同步样式更改为异步?
【发布时间】:2014-02-07 15:11:16
【问题描述】:

我正在尝试将 Dart 与 sqlite 一起使用,这个项目是 dart-sqlite

但是我发现了一个问题:它提供的API是同步风格的。代码将如下所示:

// Iterating over a result set
var count = c.execute("SELECT * FROM posts LIMIT 10", callback: (row) {
    print("${row.title}: ${row.body}");
});
print("Showing ${count} posts.");

这样的代码,我无法使用 Dart 未来的支持,代码会阻塞在 sql 操作。

我想知道如何将代码更改为异步样式?你可以在这里看到它定义了一些native 函数:https://github.com/sam-mccall/dart-sqlite/blob/master/lib/sqlite.dart#L238

_prepare(db, query, statementObject) native 'PrepareStatement';
_reset(statement) native 'Reset';
_bind(statement, params) native 'Bind';
_column_info(statement) native 'ColumnInfo';
_step(statement) native 'Step';
_closeStatement(statement) native 'CloseStatement';
_new(path) native 'New';
_close(handle) native 'Close';
_version() native 'Version';

native 函数在这里映射到一些 c++ 函数:https://github.com/sam-mccall/dart-sqlite/blob/master/src/dart_sqlite.cc

是否可以改为异步?如果可以,我该怎么办?

如果不可能,那我必须重写它,我是否必须全部重写:

  1. 飞镖文件
  2. c++ 包装文件
  3. 实际的 sqlite 驱动程序

更新:

感谢@GregLowe 的评论,Dart 的Completer 可以将回调样式转换为未来的样式,这可以让我使用Dart 的doSomething().then(...) 而不是传递回调函数。

但是在阅读了 dart-sqlite 的源码后,我意识到,在 dart-sqlite 的实现中,callback 不是基于事件的:

int execute([params = const [], bool callback(Row)]) {
  _checkOpen();
  _reset(_statement);
  if (params.length > 0) _bind(_statement, params);
  var result;
  int count = 0;
  var info = null;
  while ((result = _step(_statement)) is! int) {
    count++;
    if (info == null) info = new _ResultInfo(_column_info(_statement));
    if (callback != null && callback(new Row._internal(count - 1, info, result)) == true) {
      result = count;
      break;
    }
  }
  // If update affected no rows, count == result == 0
  return (count == 0) ? result : count;
}

即使我使用Completer,也不会提高性能。我想我可能必须先重写 c++ 代码以使其基于事件。

【问题讨论】:

  • 此代码已过时,因为编写于 2 年前。所以,你的问题仍然不实际。除非你不想自己重写它(dart-sqlite)。
  • 我修改了可以正常运行的代码,但是我不喜欢代码风格。所以如果我想要异步风格,我必须重写代码吗?是否必须重写驱动部分(c/c++)才能支持异步?
  • 您应该能够在不接触 C++ 的情况下编写包装器。看看如何在 dart:async 中使用 Completer 类。基本上你需要创建一个 Completer,立即返回 Completer.future,然后从现有回调中调用 Completer.complete(row)。
  • 回复:更新。你看过下面链接的文章,特别是关于异步扩展的那篇文章吗?即,如果 C++ API 是同步的,您可以在单独的线程中运行它,并使用消息传递与其通信。这可能是一种方法。 dartlang.org/articles/native-extensions-for-standalone-dart-vm
  • @GregLowe,非常感谢!这正是我正在寻找的,它回答了我所有的问题。可以把它作为答案,我会接受它

标签: sqlite asynchronous dart synchronous


【解决方案1】:

您应该能够在不接触 C++ 的情况下编写包装器。看看如何在 dart:async 中使用 Completer 类。基本上你需要创建一个 Completer,立即返回 Completer.future,然后从现有回调中调用 Completer.complete(row)。

回复:更新。你看过这个article,特别是关于异步扩展的一点吗?即,如果 C++ API 是同步的,您可以在单独的线程中运行它,并使用消息传递与其通信。这可能是一种方法。

【讨论】:

    【解决方案2】:

    您遇到的最大问题是 SQLite 是一个嵌入式数据库;为了处理您的查询并提供您的结果,它必须在您的流程中进行计算(和 I/O)。更重要的是,为了使其事务处理系统工作,它要么需要其连接位于thread that created it,要么需要您以序列化模式运行(性能受到影响)。

    因为这些是相当硬的约束,所以您将事物切换到异步操作模式的计划不太可能顺利进行,除非使用多个线程。由于使用多个连接会使事情变得非常复杂(因为您无法在它们之间共享某些东西,例如TEMP TABLEs),让我们考虑使用单个序列化连接;所有活动都将在 DB 级别进行序列化,但对于不经常使用 DB 的应用程序来说,这将是可以的。在 C++ 级别,您将谈论从另一个线程调用 execute,然后将消息发送回调用者线程以指示每一行和完成。 但是当你这样做时,你会受到真正的打击;特别是,您承诺一次只执行一个查询,因为当您开始同时使用两个连接并且数据库强制您使用一个连接进行序列化时,该技术会遇到语义效果方面的重大问题。

    通过在 Dart 级别管理工作线程和线程间通信,将同步-异步耦合置于 Dart 级别可能会更简单。这将使您避免显着更改 C++ 代码。我对 Dart 的了解还不够,无法在那里提供很多建议。

    我自己,我只是坚持同步连接处理,这样我就可以让我的应用程序更有用地使用多线程模式。我会受到语义的影响,并为每个线程提供自己的连接(可能是延迟分配),这样整体速度会更好,但我确实来自一个将线程视为相对重量级资源的编程社区,所以让那个什么你会。 (重线程可以做一些事情来减少他们需要的锁的数量,尝试用轻线程做是没有意义的;这是关于开销管理的。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-19
      • 2021-08-05
      • 2011-10-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多