|
|
|
@ -11,73 +11,73 @@
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<?php
|
|
|
|
|
class HelloWorld extends Thread {
|
|
|
|
|
public function __construct($world){
|
|
|
|
|
$this->world = $world;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
return sprintf("Hello %s", $this->world);
|
|
|
|
|
}
|
|
|
|
|
public function __construct($world){
|
|
|
|
|
$this->world = $world;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
return sprintf("Hello %s", $this->world);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$thread = new HelloWorld("World");
|
|
|
|
|
if ($thread->start()) {
|
|
|
|
|
printf("Thread #%lu says: %s\n", $thread->getThreadId(), $thread->join());
|
|
|
|
|
printf("Thread #%lu says: %s\n", $thread->getThreadId(), $thread->join());
|
|
|
|
|
}
|
|
|
|
|
?>
|
|
|
|
|
]]></screen>
|
|
|
|
|
]]></screen>
|
|
|
|
|
</example>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section role="examples">
|
|
|
|
|
<example>
|
|
|
|
|
<title>Synchronization</title>
|
|
|
|
|
<para>The purpose of PHP is to generate content, having Threading in the toolbox makes more content available. But content is a relative subject, for this reason good control is needed over when a Thread is allowed to execute, or indeed, forced to wait.</para>
|
|
|
|
|
<para>For example, a meta search engine may have to carry out the following:</para>
|
|
|
|
|
<orderedlist>
|
|
|
|
|
<listitem><para>Initiate a search with primary source of data</para></listitem>
|
|
|
|
|
<listitem><para>Log results of API usage for statistical analysis</para></listitem>
|
|
|
|
|
<listitem><para>Cache results to limit external API usage</para></listitem>
|
|
|
|
|
<listitem><para>Query local databases to generate page content - recent searches etc</para></listitem>
|
|
|
|
|
<listitem><para>Log results of local databse usage, HTTP usage, cache usage and possibly fall back to secondary source and goto 2;</para></listitem>
|
|
|
|
|
<listitem><para>Generate content ( HTML ) from API output, xml/json etc</para></listitem>
|
|
|
|
|
<listitem><para>Send content to client</para></listitem>
|
|
|
|
|
</orderedlist>
|
|
|
|
|
<para>In a multi-threaded design, the Thread delegated Task 2 cannot possibly run until Thread 1 has returned a result.</para>
|
|
|
|
|
<para>In an extreme design, where each Task is allocated a Thread, Threads 2,3,4,5 and even 6 can all be run concurrently, but they all depend on Thread 1 returning content.</para>
|
|
|
|
|
<para>pthreads includes an easy to use way to synchronize when Threads are allowed to execute, or forced to wait, to allow flexible, powerful multi-threading wether being used for a Search Engine or Administration ( ie. cron jobs ).</para>
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<title>Synchronization</title>
|
|
|
|
|
<para>The purpose of PHP is to generate content, having Threading in the toolbox makes more content available. But content is a relative subject, for this reason good control is needed over when a Thread is allowed to execute, or indeed, forced to wait.</para>
|
|
|
|
|
<para>For example, a meta search engine may have to carry out the following:</para>
|
|
|
|
|
<orderedlist>
|
|
|
|
|
<listitem><para>Initiate a search with primary source of data</para></listitem>
|
|
|
|
|
<listitem><para>Log results of API usage for statistical analysis</para></listitem>
|
|
|
|
|
<listitem><para>Cache results to limit external API usage</para></listitem>
|
|
|
|
|
<listitem><para>Query local databases to generate page content - recent searches etc</para></listitem>
|
|
|
|
|
<listitem><para>Log results of local databse usage, HTTP usage, cache usage and possibly fall back to secondary source and goto 2;</para></listitem>
|
|
|
|
|
<listitem><para>Generate content ( HTML ) from API output, xml/json etc</para></listitem>
|
|
|
|
|
<listitem><para>Send content to client</para></listitem>
|
|
|
|
|
</orderedlist>
|
|
|
|
|
<para>In a multi-threaded design, the Thread delegated Task 2 cannot possibly run until Thread 1 has returned a result.</para>
|
|
|
|
|
<para>In an extreme design, where each Task is allocated a Thread, Threads 2,3,4,5 and even 6 can all be run concurrently, but they all depend on Thread 1 returning content.</para>
|
|
|
|
|
<para>pthreads includes an easy to use way to synchronize when Threads are allowed to execute, or forced to wait, to allow flexible, powerful multi-threading wether being used for a Search Engine or Administration ( ie. cron jobs ).</para>
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<?php
|
|
|
|
|
class Task1 extends Thread {
|
|
|
|
|
public function __construct($params){
|
|
|
|
|
$this->params = $params;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
/* do some admin possibly here, maybe lookup local caches, check if the client is googlebot etc */
|
|
|
|
|
if ($this->ensureAvailabiilty()) {
|
|
|
|
|
$this->notify();
|
|
|
|
|
|
|
|
|
|
return $this->resultSet();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public function __construct($params){
|
|
|
|
|
$this->params = $params;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
/* do some admin possibly here, maybe lookup local caches, check if the client is googlebot etc */
|
|
|
|
|
if ($this->ensureAvailabiilty()) {
|
|
|
|
|
$this->notify();
|
|
|
|
|
|
|
|
|
|
return $this->resultSet();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class Task2 extends Thread {
|
|
|
|
|
public function __construct($task1, $params){
|
|
|
|
|
$this->task1 = $task1;
|
|
|
|
|
$this->params = $params;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
$task1 = Thread::getThread($this->task1);
|
|
|
|
|
if ($task1->wait()) {
|
|
|
|
|
$this->createLogs();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public function __construct($task1, $params){
|
|
|
|
|
$this->task1 = $task1;
|
|
|
|
|
$this->params = $params;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
$task1 = Thread::getThread($this->task1);
|
|
|
|
|
if ($task1->wait()) {
|
|
|
|
|
$this->createLogs();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tasks = array();
|
|
|
|
@ -87,70 +87,70 @@
|
|
|
|
|
/* ... */
|
|
|
|
|
|
|
|
|
|
foreach ($tasks as $id => $task) {
|
|
|
|
|
$task->start();
|
|
|
|
|
$task->start();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ... */
|
|
|
|
|
?>
|
|
|
|
|
]]></screen>
|
|
|
|
|
<para>Using the wait/notify mechanism included in pthreads hides the complexity of using Mutex and Condition Variables to synchronize Threads, greatly simplifies readability and more importantly maintainability of an idea or implementation.</para>
|
|
|
|
|
]]></screen>
|
|
|
|
|
<para>Using the wait/notify mechanism included in pthreads hides the complexity of using Mutex and Condition Variables to synchronize Threads, greatly simplifies readability and more importantly maintainability of an idea or implementation.</para>
|
|
|
|
|
</example>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section role="examples">
|
|
|
|
|
<example>
|
|
|
|
|
<title>Mutex and Condition Variables</title>
|
|
|
|
|
<para>pthreads allows direct access to ( a subset of ) both of these features. The programmer should take care to destroy Mutex and Condition Variable handles that are no longer required for their logic.</para>
|
|
|
|
|
<para>Mutex and Condition Variables are not destroyed on behalf of the programmer like other resources in PHP, as this would limit their usefulness in the context of multi-threading in SAPI environments.</para>
|
|
|
|
|
<para>They are easily stored and passed around as they are represented as long numbers. Mutex and Condition Variables persist once allocated until they are explicitly destroyed.</para>
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<example>
|
|
|
|
|
<title>Mutex and Condition Variables</title>
|
|
|
|
|
<para>pthreads allows direct access to ( a subset of ) both of these features. The programmer should take care to destroy Mutex and Condition Variable handles that are no longer required for their logic.</para>
|
|
|
|
|
<para>Mutex and Condition Variables are not destroyed on behalf of the programmer like other resources in PHP, as this would limit their usefulness in the context of multi-threading in SAPI environments.</para>
|
|
|
|
|
<para>They are easily stored and passed around as they are represented as long numbers. Mutex and Condition Variables persist once allocated until they are explicitly destroyed.</para>
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<?php
|
|
|
|
|
/* ... */
|
|
|
|
|
if (Cond::broadcast($finished)) {
|
|
|
|
|
Cond::destroy($finished);
|
|
|
|
|
Mutex::destroy($signals);
|
|
|
|
|
Cond::destroy($finished);
|
|
|
|
|
Mutex::destroy($signals);
|
|
|
|
|
}
|
|
|
|
|
?>
|
|
|
|
|
]]></screen>
|
|
|
|
|
<para>
|
|
|
|
|
]]></screen>
|
|
|
|
|
<para>
|
|
|
|
|
The snippet above shows a PHP script sending it's final broadcast to the Threads it has created and cleaning up the Condition Variable it created and used for the broadcast.
|
|
|
|
|
Other Threads that were waiting for a signal on $finished were using $signals - waiting for a signal always requires a Mutex - and as the Condition Variable is no longer valid, the accompanying Mutex is also destroyed.
|
|
|
|
|
</para>
|
|
|
|
|
<warning>
|
|
|
|
|
<para>Before the programmer attempts to use Condition Variables they should have knowledge of "Spurious Wakeups" as allowed for in the Posix Threads specification upon which pthreads is built.</para>
|
|
|
|
|
<para>
|
|
|
|
|
</para>
|
|
|
|
|
<warning>
|
|
|
|
|
<para>Before the programmer attempts to use Condition Variables they should have knowledge of "Spurious Wakeups" as allowed for in the Posix Threads specification upon which pthreads is built.</para>
|
|
|
|
|
<para>
|
|
|
|
|
A brief exaplanation of the problem is as follows:
|
|
|
|
|
</para>
|
|
|
|
|
<para>
|
|
|
|
|
</para>
|
|
|
|
|
<para>
|
|
|
|
|
A call to Cond::wait may return before the Condition Variable recieves a legitimate signal as a result of another context calling Cond::signal or Cond::broadcast.
|
|
|
|
|
Because of this behaviour, a call to wait for a signal on a Condition Variable must check a predicate ( usually a boolean value ) upon returning from the call to Cond::wait. This eliminates the risk of Spurious Wakeups.
|
|
|
|
|
</para>
|
|
|
|
|
</warning>
|
|
|
|
|
</example>
|
|
|
|
|
</para>
|
|
|
|
|
</warning>
|
|
|
|
|
</example>
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
<section role="examples">
|
|
|
|
|
<example>
|
|
|
|
|
<title>Thread Members</title>
|
|
|
|
|
<para>The members of a Thread can be of any type that PHP supports the serialization of, including programmer declared classes.</para>
|
|
|
|
|
<para>In the case where a Thread contains members that are of a programmer declared type ( class ), the programmer must include the declaration using Thread::__prepare magic method.</para>
|
|
|
|
|
<para>Thread::__prepare method is executed by pthreads in the newly created context, before the newly created context executes Thread::run. This results in correct deserialization of the programmer declared type in the new context.</para>
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<example>
|
|
|
|
|
<title>Thread Members</title>
|
|
|
|
|
<para>The members of a Thread can be of any type that PHP supports the serialization of, including programmer declared classes.</para>
|
|
|
|
|
<para>In the case where a Thread contains members that are of a programmer declared type ( class ), the programmer must include the declaration using Thread::__prepare magic method.</para>
|
|
|
|
|
<para>Thread::__prepare method is executed by pthreads in the newly created context, before the newly created context executes Thread::run. This results in correct deserialization of the programmer declared type in the new context.</para>
|
|
|
|
|
<screen><![CDATA[
|
|
|
|
|
<?php
|
|
|
|
|
class ExampleThread extends Thread {
|
|
|
|
|
public function __prepare(){
|
|
|
|
|
require_once("/path/to/inc.php");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function __construct($myType){
|
|
|
|
|
$this->myType = $myType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
if (method_exists($this->myType, "myMethod")) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public function __prepare(){
|
|
|
|
|
require_once("/path/to/inc.php");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function __construct($myType){
|
|
|
|
|
$this->myType = $myType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function run(){
|
|
|
|
|
if (method_exists($this->myType, "myMethod")) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
require_once("/path/to/inc.php");
|
|
|
|
|
/* ... */
|
|
|
|
@ -158,15 +158,15 @@
|
|
|
|
|
/* ... */
|
|
|
|
|
$example = new ExampleThread($my);
|
|
|
|
|
if ($example->start()) {
|
|
|
|
|
/* ... */
|
|
|
|
|
/* ... */
|
|
|
|
|
}
|
|
|
|
|
?>
|
|
|
|
|
]]></screen>
|
|
|
|
|
<para>
|
|
|
|
|
]]></screen>
|
|
|
|
|
<para>
|
|
|
|
|
In the example above, /path/to/inc.php declares a class, as defined by the programmer. The programmer includes the declaration in the global scope before passing an instance of the object to an instance of ExampleThread.
|
|
|
|
|
The instance of ExampleThread is able to manipulate the object and execute member functions having included the declaration using __prepare magic.
|
|
|
|
|
</para>
|
|
|
|
|
</example>
|
|
|
|
|
</para>
|
|
|
|
|
</example>
|
|
|
|
|
</section>
|
|
|
|
|
</chapter>
|
|
|
|
|
|
|
|
|
|