MySQLi wont establish connection within a pthreads worker

别等时光非礼了梦想. 提交于 2020-01-02 15:01:23

问题


Why cannot a mysqli connection be created when using a pthread worker in PHP? When establishing a mysqli and attempting to execute a query, will generatate an error

PHP Warning: mysqli::query(): Couldn't fetch mysqli in W:\workspace\Sandbox\application.php on line 47

This is my script

<?php
    // Application

    // Exceptions
    class MySQLWorkerException extends Exception {}
    class MySQLConnectionException extends Exception {}
    class MySQLQueryException extends Exception {}

    // Worker
    class SQLWorker extends Worker {
        // Class Fields
        protected   $ready;
        protected   $name;

        // MySQLi Object
        protected   $link;

        // MySQL Credentials
        private
                    $host,
                    $username,
                    $password,
                    $database,
                    $port;

        // Constructor
        function __construct($host, $username, $password, $database, $port = 3306) {
            // Set Credentials
            $this->host     = $host;
            $this->username = $username;
            $this->password = $password;
            $this->database = $database;
            $this->port     = $port;
        }

        // Methods
        public function run() {
            // Create MySQL link
            $this->link = new mysqli(
                $this->host,
                $this->username,
                $this->password,
                $this->database,
                $this->port
            );

            $this->link->query("SELECT 1");
            //var_dump($this->link);
            exit;

            // Check for connection error
            if($this->link->connect_errno) {
                throw new MySQLConnectionException('(' . $this->link->connect_errno . '): ' . $this->link->connect_error, E_WARNING);
            }

            //$this->name = sprintf("%s (%lu)", __CLASS__, $this->getThreadId());
            $this->isReady(true);
        }

        public function getConnection() {
            return $this->link;
        }

        protected function isReady($flag = null) { 
            if(is_null($flag)) {
                return ($this->ready);
            } else {
                $this->ready = $flag;
            }
        }
    }

    // MySQL Method Classes
    // STUB: class SQLQuery extends Stackable {}
    // STUB: class SQLResult extends Stackable {}
    // STUB: class SQLPrepare extends Stackable {}

    class SQLQuery extends Stackable {
        // Class Fields
        protected $complete = false;

        // SQL Query
        private $query;
        private $resultmode;

        // SQL Result
        private $result;

        // Constructor
        function __construct($query, $resultmode = MYSQLI_STORE_RESULT) {
            $this->query = $query;
            $this->resultmode = $resultmode;
        }

        // Methods
        public function run() {
            if($this->worker->isReady()) {
                if(!$this->result = mysqli_query($this->worker->link, $this->query, $this->resultmode)) {
                    throw new MySQLQueryException('(' . $this->worker->link->errno . '): ' . $this->worker->link->error, E_WARNING);
                }
            } else {
                throw new MySQLWorkerException('Worker is not ready', E_ERROR);
            }

            $this->complete = true;
            $this->notify();
        }

        public function isComplete() {
            return ($this->complete);
        }

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

        public function ReadResults() {
            $response = null;
            while($row = $this->result->fetch_assoc()) {
                $response[] = $row;
            }

            if($translateRows) {
                if(count($response) == 1) {
                    $response = end($response);

                    if(count($response) == 1) {
                        $response = end($response);
                    }
                }
            }

            $this->result->close();
            return $response;
        }
    }


    // Program
    $config = [
        'host'      => '127.0.0.1',
        'username'  => 'root',
        'password'  => '',
        'database'  => 'testdatabase',
        'port'      => 3306
    ];

    // MySQL Worker
    $worker = new SQLWorker($config['host'], $config['username'], $config['password'], $config['database'], $config['port']);
    $worker->start();

    // Create Query
    $query = new SQLQuery("SELECT username FROM users WHERE id = 1 LIMIT 1");

    // Give the query to the worker
    // $worker->stack($query);

    /* In reality we would have done a lot of shit here while the query executes. */

    // If the query isnt complete, wait up.
    $query->wait();

    // Holy shit, lets var_dump this bad boy
    // var_dump($query);

    // Shutdown the Worker Thread. We don't want any memleaks!
    $worker->shutdown();

回答1:


Here is some working code ...

<?php

class SQLWorker extends Worker {

    public function __construct($hostname, $username, $password, $database, $port = 3306) {
        $this->hostname = $hostname;
        $this->username = $username;
        $this->password = $password;
        $this->database = $database;
    }

    public function run() {
        /* the static class scope acts as a kind of thread local storage for this class */
        self::$connection = new MySQLi
            ($this->hostname, $this->username, $this->password, $this->port);
    }

    public function getConnection() {
        return self::$connection;
    }

    protected $hostname;
    protected $username;
    protected $password;
    protected $database;
    protected $port;

    protected static $connection;
}

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

    public function run() {
        /* fetch static (thread local) connection */
        $link = $this->worker->getConnection();

        if ($link) {
            /* execute query, keep result in local scope */
            $result = $link->query($this->sql);

            /* build up results as normal array */
            while (($row = $result->fetch_assoc())) {
                $rows[] = $row;
            }
        }

        /* store results in a fetchable form */
        $this->rows = $rows;
    }

    public function getResults() {
        return $this->rows;
    }
}

$pool = new Pool(4, 
    SQLWorker::class, 
    ["localhost", "username", "password", "database"]);

$pool->submit(
    $task = new SQLTask("SELECT 1"));

$pool->shutdown();

/* you have your normal array of data here */
var_dump($task->getResults());

The basic problem is that complex objects like mysqli objects will not play nice with safety implemented on pthreads objects, therefore do not write such objects to the object scope, keep them in static (thread local), method, or global scope (globals are horrible).

In addition to this, your use of synchronization is not sane. You should only ever wait for something, as explained: https://gist.github.com/krakjoe/6437782



来源:https://stackoverflow.com/questions/25426620/mysqli-wont-establish-connection-within-a-pthreads-worker

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!