如何将dart-sqlite代码从同步样式更改为异步?

时间:2014-02-07 15:11:17

标签: sqlite asynchronous dart synchronous

我正在尝试使用带有sqlite的Dart,使用此项目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';

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

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

如果不可能,我必须重写它,我是否必须重写所有:

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

  4. 更新:

    感谢@ 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 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分:1)

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

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

通过在那里管理工作线程和线程间通信,将同步 - 异步耦合置于Dart级别,可能更简单。这样可以避免必须显着更改C ++代码。我不太了解Dart能够在那里给出很多建议。

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