PHP:如何在生成器函数之后进行清理

PHP:如何在生成器函数之后进行清理,php,mysql,mysqli,Php,Mysql,Mysqli,经过一些研究,我被说服为许多任务使用生成器(更一般地说,迭代器),这些任务通常会将结果缓冲到一个数组中,因为内存使用是O(1)而不是O(n) 因此,我计划使用生成器处理通过mysqli查询的数据库结果。关于这种方法,我有两个问题无法找到答案,我希望社区能给我一些创造性的解决方案: 如果使用代码选择不完全迭代结果,是否有办法释放生成器打开的资源?使用迭代器类,可以在_destruct方法中执行此操作。但是,根据我的测试,如果生成器不能自然结束,它将不会按照迭代序列执行代码。我正在寻找解决方法,以避

经过一些研究,我被说服为许多任务使用生成器(更一般地说,迭代器),这些任务通常会将结果缓冲到一个数组中,因为内存使用是O(1)而不是O(n)

因此,我计划使用生成器处理通过mysqli查询的数据库结果。关于这种方法,我有两个问题无法找到答案,我希望社区能给我一些创造性的解决方案:

  • 如果使用代码选择不完全迭代结果,是否有办法释放生成器打开的资源?使用迭代器类,可以在_destruct方法中执行此操作。但是,根据我的测试,如果生成器不能自然结束,它将不会按照迭代序列执行代码。我正在寻找解决方法,以避免创建迭代器子类。请参阅下面的代码

  • 使用生成器或迭代器是否对数据库结果有任何好处?我的一些窥探似乎表明mysqli可能正在将resultset加载到内存中(mysqli_STORE_RESULT),从而破坏了迭代器的功能。如果结果没有缓冲,我很好奇,当它们的结果集同时被迭代(获取)时,是否可以执行多个查询(考虑嵌套循环,在嵌套循环中迭代一组项,然后为每个父项查询子项)。这看起来像是数据库游标可能会在整个迭代过程中锁定

  • 例子 下面是我所说的清理的简化。在我的测试中,只有当整个结果被迭代时,结果才会被释放。如果消费循环中存在异常或中断,则永远不会释放结果。也许我想得太多了,垃圾收集器足够好了

    function query($mysqli, $sql){
      $result = $mysqli->query($sql);
      foreach($result as $row){
        yield $row;
      }
      $result->free(); //Never reached if break, exception, take first n rows, etc.
    }
    
    tl;dr is我只是好奇如何释放生成器使用的资源,以及随后用于数据库访问的生成器是否真的保存了任何内容,或者结果是否被缓冲

    更新 这里的()看起来像默认情况下的PHP缓冲区查询,可能会破坏生成器的功能。尽管可以这样说,只缓冲一个数组比创建一个缓冲数组的副本然后拥有两个缓冲集要好


    我正在寻找在这件事上有经验的人参与进来。谢谢你的想法

    我可能会晚一点参加聚会,但是如果您正在使用生成器,并且需要在完成时进行清理(比如在完成所有循环之前中断父循环),您可以在finally块中使用try/catch/finally进行清理:

    function query($mysqli, $sql){
      $result = $mysqli->query($sql);
      foreach($result as $i => $row) {
        if ($i + 1 === $result->num_rows) {
          $result->free();
        }
        yield $row;
      }
    }
    
    function query($mysqli, $sql) {
      $result = $mysqli->query($sql);
      try {
        if ($result) {
          foreach($result as $row) {
            yield $row;
          }
        }
      } catch (Exception $e) {
        throw $e; // send this up the stack (or you could handle here)
      } finally {
        $result->free(); // clean up when the loop is finished.
      }
    }
    

    下面介绍如何检测循环中断,以及如何在中断后处理或清理

    function generator()
    {
        $complete = false;
        try {
    
            while (($result = some_function())) {
                yield $result;
            }
            $complete = true;
    
        } finally {
            if (!$complete) {
                // cleanup when loop breaks 
            } else {
                // cleanup when loop completes
            }
        }
    
        // Do something only after loop completes
    }
    

    不幸的是,与上面的初始解决方案相比,这并没有什么好处,因为在这两种情况下都必须完全迭代结果集。添加
    finally
    块使得
    catch
    是可选的。如果不处理异常,则不应捕获它。