2

I am currently wanting to learn and implement pthreads - github. I have compiled php7 with the necessary components and verified that thread-safety is enabled. My current example code has 4 child classes that belong to parent class Canine. How should I call the Thread class accordingly to execute bark() from all classes shown below at the same? Keeping in mind the ability to scale when classes grow from 4 to 100. Pools? Workers?

 class Database {
    private $_host;
    private $_user;
    private $_pass;
    private $_dbname;
    private $_dsn;
    private $_options;
    public static $pdo;
    private $error;

    private $stmt;

    public function __construct() {
        $this->_host   = 'xxxxx';
        $this->_user   = 'xxxxx';
        $this->_pass   = 'xxxxx';
        $this->_dbname = 'xxxxx';
        $this->_dsn    = 'mysql:host=' . $this->_host . ';dbname=' . $this->_dbname;
        // Set options
        $this->_options = array(
            PDO::MYSQL_ATTR_LOCAL_INFILE => true,
            PDO::ATTR_PERSISTENT         => true,
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_TIMEOUT            => 1,
        );
    }

    public function __sleep() {
        return array('_dsn', '_user', '_pass');
    }

    public function __wakeup() {
        $this->connect();
    }

    public function connect() {
        try {
            return self::$pdo = new PDO($this->_dsn, $this->_user, $this->_pass, $this->_options);
        } catch (PDOException $e) {
            $this->error = $e->getMessage();
        }
    }
}

class DataAccessObject {
    protected $database;

    public function __construct() {
        //Create Instance of DB
        $this->database = (new Database())->connect();
    }

    public function insertQuery($query) {
        try {
            $select = $this->database->prepare($query);
            $select->execute();
            $results = $select->fetchAll(PDO::FETCH_ASSOC);
        } catch (Exception $e) {
            echo "Failed: " . $e->getMessage();
        }
        return true;
    }
}

class Helper extends DataAccessObject {
//code elided
}

abstract class Canine extends Helper {
    abstract public function bark();
}

//Child Classes

class GoldenRitriever extends Canine {
    public function bark() { echo 'arf-arf!<br>'; }
}

class Pitbull extends Canine {
    public function bark() { echo 'ruff-ruff!<br>'; }
}

class GermanShepherd extends Canine {
    public function bark() { echo 'wao-wao!<br>'; }
}

class LabradorRetriever extends Canine {
    public function bark() { echo 'woof-woof!<br>'; }
}

Creating instances and invoking `bark()`

$golden_ritriever = new GoldenRitriever();
$golden_ritriever->bark();

$pitbull = new Pitbull();
$pitbull->bark();

$german_shepherd = new GermanShepherd();
$german_shepherd->bark();

$labrador_retriever = new LabradorRetriever();
$labrador_retriever->bark();
Code_Ed_Student
  • 1,180
  • 6
  • 27
  • 67

1 Answers1

4

I imagine that your problem is with the architecture of the solution i.e. you will have to extend the Thread class somewhere and are not sure where.

Assuming that the class structure (Helper, DataAccessObject) is final and that it does not make sense to extend DataAcessObject with Thread I would do it the following way.

class ThreadedCanine extends Thread {
    private $canine;
    public function __construct(Canine $canine) {
        $this->canine = $canine;
    }

    public function run() {
        $this->canine->bark();
    }
}

$threads = [
    new ThreadedCanine(new GoldenRitriever()),
    new ThreadedCanine(new Pitbull()),
    new ThreadedCanine(new GermanShepherd()),
    new ThreadedCanine(new LabradorRetriever()),
];

foreach($threads as $thread) {
    $thread->start();
}

foreach($threads as $thread) {
    $thread->join();
}

I think this is a good way because it does not force a Canine to be a Thread as it might not need to be a thread all the time.

EDIT

I forgot to mention, in the original answer, that if you will want to scale to 100s of threads, yes, you should be using the Pool and Worker classes to manage that (the docs even recommend to use Worker instead of Thread), otherwise launching 100 threads simultaneously will not be good (and gets worse if you have multiple clients) :P

Once you get a better grasp of using Threads you can move to those scenarios.

Hope it helps :)

Ricardo Velhote
  • 4,630
  • 1
  • 24
  • 30
  • Great answer. It is starting to make more sense. I am getting an error `You cannot serialize or unserialize PDO instances`. Not sure the reason of the error. Running without any threads doesn't give such error. I added the entirety of my `Database` class and `DataAccessObject` to the post. – Code_Ed_Student Sep 08 '16 at 00:32
  • You also have a MySQL connection resource in the `DataAccessObject` class. Try putting that one to sleep to. By the way, did you check https://github.com/krakjoe/pthreads/blob/master/examples/MySQLi.php ? It could be important to follow that best practice once you get a better grasp of phtreads ;) – Ricardo Velhote Sep 08 '16 at 08:57
  • Thanks. I did see the example for managing connections with mysqli. Too bad they dont have an example with PDO. How would I put the DB resource to sleep on `DataAccessObject`? – Code_Ed_Student Sep 08 '16 at 10:43
  • In the `Database` class you have the `__sleep` and `__wakeup` method. I was suggesting you do the same. About the example in the docs, even though it's related to MySQLi you can use it for PDO. The problem you are facing is about serializing and unserializing resources which both PDO and MySQLi have (i.e. the resource is the connection to the database). – Ricardo Velhote Sep 08 '16 at 11:14