我在这里读到的地方,文件描述符和数据库连接在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);
}
}
?>
答案 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获取数据并不困难,在您阅读了两个主要链接之后,其他逻辑应该变得明显: