代码之家  ›  专栏  ›  技术社区  ›  Freewind thk

如何将dartsqlite代码从同步样式更改为异步样式?

  •  0
  • Freewind thk  · 技术社区  · 11 年前

    我正在尝试将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';
    

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

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

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

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

    更新:

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

    但是在阅读了dart-sqlite的源代码之后,我意识到,在dart-sqlte的实现中 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;
    }
    

    即使我使用 完成人 ,这不会提高性能。我想我可能必须重写c++代码,使其首先基于事件。

    2 回复  |  直到 11 年前
        1
  •  1
  •   Greg Lowe    11 年前

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

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

        2
  •  1
  •   Donal Fellows    11 年前

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

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

    通过管理工作线程和线程间通信,将同步异步耦合放在Dart级别,这样做可能更简单。这将使您避免对C++代码进行重大更改。我不太了解达特,无法在那里提供很多建议。

    我自己,我只是坚持同步连接处理,这样我可以让我的应用程序更有效地使用多线程模式。我将在语义方面取得成功,并为每个线程提供自己的连接(可能是延迟分配的),这样总体速度会更好,但我确实来自一个将线程视为相对重量级资源的编程社区,所以请尽可能利用这一点。(重线程可以减少所需锁的数量,但尝试使用轻线程是没有意义的;这与开销管理有关。)