php pcntl_fork():在childs中使用parent的sqlite3对象?

时间:2013-12-06 13:47:29

标签: php multithreading sqlite fork

我在这里读到的地方,文件描述符和数据库连接在fork()创建的父和子之间共享,这可能会导致问题。

哪些是有问题的条件?

我想fork()多个子节点并在每个子节点中使用相同的SQLITE3数据库。

使用在fork()之前创建的一个SQLITE3对象是否可以保存? 这个对象被复制到孩子身上,但这是什么意思?

所有子节点是否可以使用父节点对象的副本同时访问同一个数据库(读取和写入)?

<?
$db = new SQLite3("file.sqlite");

for( $i=0; $i<10; $i++ ) {

    $p = fork();

    // As a child:
    if( !$p ) {

       // do some reads/writes
       $db->query("SELECT a,b, FROM T..");

       $db->query("INSERT INTO T SELECT ...");
       $db->query("UPDATE T SET a=5 WHERE ...");

       exit(0);
    }
}
?>

或者每个孩子都需要创建自己的对象(并分别连接到数据库)吗?我可以在每个孩子中关闭父母的联系吗?

<?
$db = new SQLite3("file.sqlite");

for( $i=0; $i<10; $i++ ) {

    $p = fork()

    // As a child:
    if( !$p ) {

       // Is this necessary?
       $db->close();
       $mydb = new SQLite3("file.sqlite");

       // do some reads/writes
       $mydb->query("SELECT a,b, FROM T..");

       $mydb->query("INSERT INTO T SELECT ...");
       $mydb->query("UPDATE T SET a=5 WHERE ...");

       exit(0);
    }
}
?>

3 个答案:

答案 0 :(得分:1)

当forks必须访问同一个SQLite文件时,不要在分叉代码中使用SQLite(或任何其他基于文件的数据库系统)。

如果您写入SQLite数据库,SQLite库必须锁定整个文件。写操作完成后,锁定被释放。其他进程必须等到释放此锁之后才能写入此数据库。

在您的示例中,SQLite序列化所有分支。所有这些都必须等到最后一个fork完成写入数据库。

而不是SQLite,你应该使用一些'真正的'数据库系统,如MySQL或PostgreSQL。

答案 1 :(得分:1)

我有完全相同的问题。由于还没有真正的答案,这就是我找到的。 你应该绝对在分叉后打开每个Child和Parent中的DB-Connection 。否则你可能会破坏你的数据库文件(我已经完成了......)。

来源:

p.s。:所有人都建议不要在多线程应用程序中使用SQlite。是的,你必须要小心。是的,当你知道,你在做什么是可能的。不,有时没有更好的替代选择。

p.p.s:我在这里谈论php-cli。我不会尝试在网络服务器环境中使用pcntl_fork。

答案 2 :(得分:1)

忽略工具箱中最强大的工具

是错误的

现代计算世界才有效,因为计算机能够进行多任务处理,只要有人提到并发问题或线程,每个人都会在他们的腿之间甩尾。

并发工作,婊子......

如果程序员无法想到实现并发执行的方法; 这是程序员的失败,而不是并发执行的失败,而不是你想要使用的数据库驱动程序的失败,而不是想象力的失败。

手头的任务......

从多个线程尝试使用SQLite数据库并不聪明,但请注意:

这绝对没有说明可以使用多少个线程,只有那个SQLite可能只限于一个

这意味着只需几分钟的思考;您的代码可以使用所需数量的线程,只要它有一种方法将读写操作分配给另一个线程,并在必要时检索它们的响应。

如何

这绝不是最终的实施,它是朝着正确方向发展的重要推动力,让你以正确的方式思考。

我不确定操作的性质或数据,以下是该想法的最简单版本,足以让您入门,或诱使您阅读它;)

<?php

class DataReader extends Stackable {
    public function __construct($sql) {
        $this->sql = $sql;
    }

    public function run() {
        $result = $this->worker
            ->getHandle()
            ->query($this->sql);

        /* note, build normal array in method scope */
        $data = [];
        if ($result) {
            while (($row = $result->fetchArray(SQLITE3_ASSOC)))
                $data[] = $row;
            /* write the whole array at once to the object */
            $this->result = $data;
        }

        var_dump($this);
    }

    public function getResult() {
        return $this->result;
    }

    protected $sql;
    protected $result;
}

class DataWriter extends Stackable {

    public function __construct($sql) {
        $this->sql = $sql;
    }

    public function run() {
        $result = $this->worker
            ->getHandle()
            ->query($this->sql);
        if ($result) {
            $this->result = true;
        } else $this->result = false;
        var_dump($this);
    }

    public function getResult() {
        return $this->result;
    }

    protected $sql;
    protected $result;
}

class DataWorker extends Worker {

    public function __construct($db, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $key = null) {
        $this->db = $db;
        $this->flags = $flags;
        $this->key = $key;
    }

    public function run() {
        self::$handle = new SQLite3(
            $this->db, $this->flags, $this->key);

        if (!self::$handle) {
            throw new RuntimeException(
                "The SQLite3 database \"{$this->db}\" could not be opened");
        }
    }

    public function getHandle() {
        return self::$handle;
    }

    protected $db;
    protected $flags;
    protected $key;

    protected static $handle;
}

/* extending from Pool makes all things simple */
class DataManager extends Pool {

    public function __construct($db, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $key = null) {
        /* creates a pool of 1, which opens the SQLite3 database */
        parent::__construct(1, DataWorker::class, [
            $db, $flags, $key
        ]);
    }

    protected $db;
    protected $flags;
    protected $key;
}

$worker = new DataManager("test.db");

$worker->submit(
    new DataReader("SELECT * FROM `test`"));

$worker->submit(
    new DataWriter("INSERT INTO `test` VALUES('140');"));
?>

从DataReader获取数据并不困难,在您阅读了两个主要链接之后,其他逻辑应该变得明显:

https://github.com/krakjoe/pthreads/blob/master/README.md